-
Install Docker and Docker Compose if they are not already installed on your system.
-
Clone the project repository:
git clone https://github.com/Djama1GIT/avito.git
cd avito
- Start the project:
docker-compose up --build
After starting the project, you can access the Swagger user interface at: http://localhost:8000/swagger/index.html.
In Swagger, you can view the available endpoints and their parameters, and also make requests to the API.
If the tables in the database were not created automatically, use:
make init_sql
Method for creating a segment. Accepts the segment's slug (name)
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "example"}'
200: {"slug":"example"}
400: {"message":"pq: duplicate key value violates unique constraint "segments_pkey""}
Method for deleting a segment. Accepts the segment's slug (name)
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "example"}'
200: {"slug":"example"}
400: {"message":"segment with slug example does not exist"}
Method for adding a user to a segment. Accepts a list of segment slugs to add to the user, a list of segment slugs to remove from the user, and the user's ID
curl -X PATCH http://127.0.0.1:8000/api/segments/ \
-d '{"user_id": 1, "segments_to_add": ["AVITO_VOICE_MESSAGES"], "segments_to_delete": ["AVITO_PERFORMANCE_VAS", "AVITO_DISCOUNT_30"]}'
200: {"user_id":1}
400: {"message":"error occurred while processing segment to add 'AVITO_VOICE_MESSAGES': pq: duplicate key value violates unique constraint "user_segments_pkey""}
400: {"message":"error occurred while checking segment to delete existence 'AVITO_PERFORMANCE_VAS': user(1) is not in this segment"}%
Method for retrieving a user's active segments. Accepts the user's ID
curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 1}'
200: {"segments":["AVITO_VOICE_MESSAGES"],"user_id":1}
200: {"segments":[],"user_id":1}
A method of obtaining the history of a user's entry/exit from a segment over a certain period in csv format.
curl -X GET http://127.0.0.1:8000/api/users/history/ -d '{"user_id": 1, "year_month": "2023-08"}'
200: {"report":"http://localhost:8000/files/reports/user_history_2023-08_1.csv","user_id":1}
400: {"message":"invalid YearMonth"}
400: {"message":"json: cannot unmarshal string into Go struct field UserHistory.user_id of type int"}
Method for adding a user to a segment with the ability to set TTL
curl -X PATCH http://127.0.0.1:8000/api/segments/ -d \
'{"user_id": 1, "segments_to_add": ["AVITO_VOICE_MESSAGES"], "segments_to_add_expiration":"2023-08-29 00:00:00", "segments_to_delete": []}'
200: {"user_id":1}
400: {"message":"parsing time "2023-08-31 00:00:" as "2006-01-02 15:04:05": cannot parse "" as "05""}
Method for deleting expired segments of a user
Automatically called using cron every minutecurl -X DELETE http://127.0.0.1:8000/api/users/expired-segments/
200: empty result
An option to specify the percentage of users who will automatically be included in the segment has been added to the segment creation method.
There are many different ways this functionality could be implemented, and after some consideration, the following decision was made:Inclusion in the segment (or not) depends on a function that:
-
Takes as input the user ID, segment, and probability.
-
Hashes them.
-
Using the formula and specified probability, returns true or false, always the same.
Nuances:
- The number of users included in the segment may vary by up to ~1% from (specified probability * total number of users).
I deemed this acceptable, as this is a service for analysts, and quality is more important than quantity here.
Specifically, this function ensures maximum diversification of users included in the segment,
as random users from the entire population will be included in the segment,
without any particular user being included too often or too infrequently,
which would be the case with a simpler implementation.
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_VOICE_MESSAGES", "percentage": 50}'
200: {"slug": "AVITO_VOICE_MESSAGES"}
400: {"message": "invalid percentage"}
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_VOICE_MESSAGES"}' -w '\n'
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_PERFORMANCE_VAS"}' -w '\n'
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_DISCOUNT_30"}' -w '\n'
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_DISCOUNT_50"}' -w '\n'
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_DELIVERY_FEATURE"}' -w '\n'
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_CLOUD_FEATURE"}' -w '\n'
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_UNDEFINED_FEATURE"}' -w '\n'
{"slug":"AVITO_VOICE_MESSAGES"}
{"slug":"AVITO_PERFORMANCE_VAS"}
{"slug":"AVITO_DISCOUNT_30"}
{"slug":"AVITO_DISCOUNT_50"}
{"slug":"AVITO_DELIVERY_FEATURE"}
{"slug":"AVITO_CLOUD_FEATURE"}
{"slug":"AVITO_UNDEFINED_FEATURE"}
curl -X PATCH http://127.0.0.1:8000/api/segments/ \
-d '{"user_id": 1, "segments_to_add": ["AVITO_VOICE_MESSAGES", "AVITO_PERFORMANCE_VAS", "AVITO_DISCOUNT_30", "AVITO_DISCOUNT_50"], "segments_to_delete": []}' -w '\n'
curl -X PATCH http://127.0.0.1:8000/api/segments/ \
-d '{"user_id": 1, "segments_to_add": ["AVITO_DELIVERY_FEATURE", "AVITO_CLOUD_FEATURE", "AVITO_UNDEFINED_FEATURE"], "segments_to_delete": ["AVITO_VOICE_MESSAGES", "AVITO_PERFORMANCE_VAS", "AVITO_DISCOUNT_30"]}' -w '\n'
curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 1}' -w '\n'
curl -X PATCH http://127.0.0.1:8000/api/segments/ \
-d '{"user_id": 1, "segments_to_add": [], "segments_to_delete": ["AVITO_DISCOUNT_50", "AVITO_DELIVERY_FEATURE", "AVITO_CLOUD_FEATURE", "AVITO_UNDEFINED_FEATURE"]}' -w '\n'
curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 1}' -w '\n'
{"user_id":1}
{"user_id":1}
{"segments":["AVITO_CLOUD_FEATURE","AVITO_DELIVERY_FEATURE","AVITO_DISCOUNT_50","AVITO_UNDEFINED_FEATURE"],"user_id":1}
{"user_id":1}
{"segments":[],"user_id":1}
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_VOICE_MESSAGES"}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_PERFORMANCE_VAS"}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_DISCOUNT_30"}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_DISCOUNT_50"}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_DELIVERY_FEATURE"}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_CLOUD_FEATURE"}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_UNDEFINED_FEATURE"}' -w '\n'
{"slug":"AVITO_VOICE_MESSAGES"}
{"slug":"AVITO_PERFORMANCE_VAS"}
{"slug":"AVITO_DISCOUNT_30"}
{"slug":"AVITO_DISCOUNT_50"}
{"slug":"AVITO_DELIVERY_FEATURE"}
{"slug":"AVITO_CLOUD_FEATURE"}
{"slug":"AVITO_UNDEFINED_FEATURE"}
curl -X GET http://127.0.0.1:8000/api/users/history/ -d '{"user_id": 1, "year_month": "2023-08"}'
{"report":"http://localhost:8000/files/reports/user_history_2023-08_1.csv","user_id":1}
user_history_2023-08_1.csv:
1,AVITO_VOICE_MESSAGES,добавление,2023-08-29 20:33:11
1,AVITO_PERFORMANCE_VAS,добавление,2023-08-29 20:33:11
1,AVITO_DISCOUNT_30,добавление,2023-08-29 20:33:11
1,AVITO_DISCOUNT_50,добавление,2023-08-29 20:33:11
1,AVITO_DELIVERY_FEATURE,добавление,2023-08-29 20:33:12
1,AVITO_CLOUD_FEATURE,добавление,2023-08-29 20:33:12
1,AVITO_UNDEFINED_FEATURE,добавление,2023-08-29 20:33:12
1,AVITO_VOICE_MESSAGES,удаление,2023-08-29 20:33:12
1,AVITO_PERFORMANCE_VAS,удаление,2023-08-29 20:33:12
1,AVITO_DISCOUNT_30,удаление,2023-08-29 20:33:12
1,AVITO_DISCOUNT_50,удаление,2023-08-29 20:33:12
1,AVITO_DELIVERY_FEATURE,удаление,2023-08-29 20:33:12
1,AVITO_CLOUD_FEATURE,удаление,2023-08-29 20:33:12
1,AVITO_UNDEFINED_FEATURE,удаление,2023-08-29 20:33:12
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_VOICE_MESSAGES"}' -w '\n'
curl -X PATCH http://127.0.0.1:8000/api/segments/ \
-d '{"user_id": 1, "segments_to_add": ["AVITO_VOICE_MESSAGES"], "segments_to_add_expiration":"1970-08-31 00:00:00", "segments_to_delete": []}' -w '\n'
curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 1}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/users/expired-segments/ -w '\n'
curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 1}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_VOICE_MESSAGES"}' -w '\n'
{"slug":"AVITO_VOICE_MESSAGES"}
{"user_id":1}
{"segments":["AVITO_VOICE_MESSAGES"],"user_id":1}
{"segments":[],"user_id":1}
{"slug":"AVITO_VOICE_MESSAGES"}
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "example-slug", "percentage": 27}' -w '\n'
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "example-slug2", "percentage": 27}' -w '\n'
curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 420}' -w '\n'
curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 421}' -w '\n'
curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 422}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "example-slug"}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "example-slug2"}' -w '\n'
{"slug":"example-slug"}
{"slug":"example-slug2"}
{"segments":["example-slug"],"user_id":420}
{"segments":["example-slug","example-slug2"],"user_id":421}
{"segments":[],"user_id":422}
{"slug":"example-slug"}
{"slug":"example-slug2"}
-
Установите Docker и Docker Compose, если они еще не установлены на вашей системе.
-
Клонируйте репозиторий проекта:
git clone https://github.com/Djama1GIT/avito.git
cd avito
- Запустите проект:
docker-compose up --build
После запуска проекта вы сможете получить доступ к пользовательскому интерфейсу Swagger по адресу:
http://localhost:8000/swagger/index.html.
В Swagger вы сможете просмотреть доступные ручки и их параметры, а также выполнять запросы к API.
Если таблицы в БД не создались автоматически используйте:
make init_sql
Метод создания сегмента. Принимает slug (название) сегмента
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "example"}'
200: {"slug":"example"}
400: {"message":"pq: duplicate key value violates unique constraint "segments_pkey""}
Метод удаления сегмента. Принимает slug (название) сегмента
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "example"}'
200: {"slug":"example"}
400: {"message":"segment with slug example does not exist"}
Метод добавления пользователя в сегмент. Принимает список slug (названий) сегментов которые нужно добавить пользователю, список slug (названий) сегментов которые нужно удалить у пользователя, id пользователя
curl -X PATCH http://127.0.0.1:8000/api/segments/ \
-d '{"user_id": 1, "segments_to_add": ["AVITO_VOICE_MESSAGES"], "segments_to_delete": ["AVITO_PERFORMANCE_VAS", "AVITO_DISCOUNT_30"]}'
200: {"user_id":1}
400: {"message":"error occurred while processing segment to add 'AVITO_VOICE_MESSAGES': pq: duplicate key value violates unique constraint "user_segments_pkey""}
400: {"message":"error occurred while checking segment to delete existence 'AVITO_PERFORMANCE_VAS': user(1) is not in this segment"}%
Метод получения активных сегментов пользователя. Принимает на вход id пользователя
curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 1}'
200: {"segments":["AVITO_VOICE_MESSAGES"],"user_id":1}
200: {"segments":[],"user_id":1}
Метод получения истории попадания/выбывания пользователя из сегмента за определенный период в формате csv
curl -X GET http://127.0.0.1:8000/api/users/history/ -d '{"user_id": 1, "year_month": "2023-08"}'
200: {"report":"http://localhost:8000/files/reports/user_history_2023-08_1.csv","user_id":1}
400: {"message":"invalid YearMonth"}
400: {"message":"json: cannot unmarshal string into Go struct field UserHistory.user_id of type int"}
Метод добавления пользователя в сегмент с возможностью задавать TTL
curl -X PATCH http://127.0.0.1:8000/api/segments/ -d \
'{"user_id": 1, "segments_to_add": ["AVITO_VOICE_MESSAGES"], "segments_to_add_expiration":"2023-08-29 00:00:00", "segments_to_delete": []}'
200: {"user_id":1}
400: {"message":"parsing time "2023-08-31 00:00:" as "2006-01-02 15:04:05": cannot parse "" as "05""}
Метод удаления истекших сегментов пользователя
Вызывается автоматически при помощи cron каждую минутуcurl -X DELETE http://127.0.0.1:8000/api/users/expired-segments/
200: empty result
В методе создания сегмента, добавлена опция указания процента пользователей, которые будут попадать в сегмент автоматически.
Есть множество различных вариантов, как можно было бы реализовать этот функционал, и в итоге после некоторых раздумий было принято такое решение:Попадание в сегмент(или нет) зависит от функции, которая:
-
Принимает на вход id пользователя, сегмент, вероятность.
-
Хеширует их
-
По формуле, с указанной вероятность, возвращает true или false, причем всегда одинаково.
Ньюансы:
-
Количество пользователей попавших в сегмент может изменяться до ~1%
от (указанной вероятности * количество всех пользователей)
Я посчитал это допустимым, т.к. это сервис для аналитиков, и тут важнее качество, а не количество
А именно благодаря такой функции можно обеспечить максимальную диверсификацию попавших в сегмент пользователей,
т.к. в сегмент будут попадать случайные пользователи из всей массы, и при этом не будет такого,
что определенный пользователь попадает часто в какие-то сегменты, а другой наоборот,
если бы была использована более простая реализация.
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_VOICE_MESSAGES", "percentage": 50}'
200: {"slug": "AVITO_VOICE_MESSAGES"}
400: {"message": "invalid percentage"}
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_VOICE_MESSAGES"}' -w '\n'
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_PERFORMANCE_VAS"}' -w '\n'
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_DISCOUNT_30"}' -w '\n'
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_DISCOUNT_50"}' -w '\n'
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_DELIVERY_FEATURE"}' -w '\n'
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_CLOUD_FEATURE"}' -w '\n'
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_UNDEFINED_FEATURE"}' -w '\n'
{"slug":"AVITO_VOICE_MESSAGES"}
{"slug":"AVITO_PERFORMANCE_VAS"}
{"slug":"AVITO_DISCOUNT_30"}
{"slug":"AVITO_DISCOUNT_50"}
{"slug":"AVITO_DELIVERY_FEATURE"}
{"slug":"AVITO_CLOUD_FEATURE"}
{"slug":"AVITO_UNDEFINED_FEATURE"}
curl -X PATCH http://127.0.0.1:8000/api/segments/ \
-d '{"user_id": 1, "segments_to_add": ["AVITO_VOICE_MESSAGES", "AVITO_PERFORMANCE_VAS", "AVITO_DISCOUNT_30", "AVITO_DISCOUNT_50"], "segments_to_delete": []}' -w '\n'
curl -X PATCH http://127.0.0.1:8000/api/segments/ \
-d '{"user_id": 1, "segments_to_add": ["AVITO_DELIVERY_FEATURE", "AVITO_CLOUD_FEATURE", "AVITO_UNDEFINED_FEATURE"], "segments_to_delete": ["AVITO_VOICE_MESSAGES", "AVITO_PERFORMANCE_VAS", "AVITO_DISCOUNT_30"]}' -w '\n'
curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 1}' -w '\n'
curl -X PATCH http://127.0.0.1:8000/api/segments/ \
-d '{"user_id": 1, "segments_to_add": [], "segments_to_delete": ["AVITO_DISCOUNT_50", "AVITO_DELIVERY_FEATURE", "AVITO_CLOUD_FEATURE", "AVITO_UNDEFINED_FEATURE"]}' -w '\n'
curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 1}' -w '\n'
{"user_id":1}
{"user_id":1}
{"segments":["AVITO_CLOUD_FEATURE","AVITO_DELIVERY_FEATURE","AVITO_DISCOUNT_50","AVITO_UNDEFINED_FEATURE"],"user_id":1}
{"user_id":1}
{"segments":[],"user_id":1}
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_VOICE_MESSAGES"}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_PERFORMANCE_VAS"}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_DISCOUNT_30"}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_DISCOUNT_50"}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_DELIVERY_FEATURE"}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_CLOUD_FEATURE"}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_UNDEFINED_FEATURE"}' -w '\n'
{"slug":"AVITO_VOICE_MESSAGES"}
{"slug":"AVITO_PERFORMANCE_VAS"}
{"slug":"AVITO_DISCOUNT_30"}
{"slug":"AVITO_DISCOUNT_50"}
{"slug":"AVITO_DELIVERY_FEATURE"}
{"slug":"AVITO_CLOUD_FEATURE"}
{"slug":"AVITO_UNDEFINED_FEATURE"}
curl -X GET http://127.0.0.1:8000/api/users/history/ -d '{"user_id": 1, "year_month": "2023-08"}'
{"report":"http://localhost:8000/files/reports/user_history_2023-08_1.csv","user_id":1}
user_history_2023-08_1.csv:
1,AVITO_VOICE_MESSAGES,добавление,2023-08-29 20:33:11
1,AVITO_PERFORMANCE_VAS,добавление,2023-08-29 20:33:11
1,AVITO_DISCOUNT_30,добавление,2023-08-29 20:33:11
1,AVITO_DISCOUNT_50,добавление,2023-08-29 20:33:11
1,AVITO_DELIVERY_FEATURE,добавление,2023-08-29 20:33:12
1,AVITO_CLOUD_FEATURE,добавление,2023-08-29 20:33:12
1,AVITO_UNDEFINED_FEATURE,добавление,2023-08-29 20:33:12
1,AVITO_VOICE_MESSAGES,удаление,2023-08-29 20:33:12
1,AVITO_PERFORMANCE_VAS,удаление,2023-08-29 20:33:12
1,AVITO_DISCOUNT_30,удаление,2023-08-29 20:33:12
1,AVITO_DISCOUNT_50,удаление,2023-08-29 20:33:12
1,AVITO_DELIVERY_FEATURE,удаление,2023-08-29 20:33:12
1,AVITO_CLOUD_FEATURE,удаление,2023-08-29 20:33:12
1,AVITO_UNDEFINED_FEATURE,удаление,2023-08-29 20:33:12
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_VOICE_MESSAGES"}' -w '\n'
curl -X PATCH http://127.0.0.1:8000/api/segments/ \
-d '{"user_id": 1, "segments_to_add": ["AVITO_VOICE_MESSAGES"], "segments_to_add_expiration":"1970-08-31 00:00:00", "segments_to_delete": []}' -w '\n'
curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 1}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/users/expired-segments/ -w '\n'
curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 1}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "AVITO_VOICE_MESSAGES"}' -w '\n'
{"slug":"AVITO_VOICE_MESSAGES"}
{"user_id":1}
{"segments":["AVITO_VOICE_MESSAGES"],"user_id":1}
{"segments":[],"user_id":1}
{"slug":"AVITO_VOICE_MESSAGES"}
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "example-slug", "percentage": 27}' -w '\n'
curl -X POST http://127.0.0.1:8000/api/segments/ -d '{"slug": "example-slug2", "percentage": 27}' -w '\n'
curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 420}' -w '\n'
curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 421}' -w '\n'
curl -X GET http://127.0.0.1:8000/api/segments/ -d '{"user_id": 422}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "example-slug"}' -w '\n'
curl -X DELETE http://127.0.0.1:8000/api/segments/ -d '{"slug": "example-slug2"}' -w '\n'
{"slug":"example-slug"}
{"slug":"example-slug2"}
{"segments":["example-slug"],"user_id":420}
{"segments":["example-slug","example-slug2"],"user_id":421}
{"segments":[],"user_id":422}
{"slug":"example-slug"}
{"slug":"example-slug2"}