-
Notifications
You must be signed in to change notification settings - Fork 0
Модель данных
Хранение данных о пользователе системы
- name - название (String, до 50 байтов)
- email - адрес электронной почты (String, до 129 байтов)
- password - пароль (String - до 64 байтов)
- created_at - Дата и время регистрации (Datetime - 8 байт)
Хранение созданных пользователем маршрутов
- name - название (String, до 50 байтов)
- description - описание (String, до 2000 байт)
- length - длина в метрах (Float, 4 байта)
- duration - продолжительность в минутах (Integer, 4 байта)
- created_at - дата создания (Datetime, 8 байт)
Хранение точек интереса на карте
- name - название (String, до 50 байтов)
- description - описание (String, до 2000 байт)
Хранение данных о прогулках пользователей по маршруту
- start_datetime - дата и время начала (Datetime, 8 байтов)
- end_datetime - дата и время окончания (Datetime, 8 байтов)
- length - длина в метрах (Float, 4 байта)
Хранение промежуточных положений пользователя в ходе прогулки
- location - местоположение (Point, 16 байтов)
- time - дата и время (Datetime, 8 байтов)
Хранение данных о местоположении перекрестков на карте
- street_count - количество улиц (Integer, 4 байта)
- location - местоположение (Point, 16 байтов)
Хранение ссылки на загруженные в систему изображения
- name - название изображения (String, до 50 байтов)
- uri - uri изображения (String, до 100 байтов)
Возьмём средний объём текста в полях без чёткого объёма как 2/3 с округлением в верх от максимального. При расчетах подразумевается, что каждому узлу и связи нужен идентификатор типа Integer размером 4 байт.
- User - 4 + 34 + 86 +43 + 8 = 175 байт на объект
- Route - 4 + 34 + 1334 + 4 + 4 + 8 = 1388 байт на объект
- PointOfInterest - 4 + 34 + 1334 = 1372 байт на объект
- Walk - 4 + 8 + 8 + 4 = 24 байт на объект
- Dot - 4 + 16 + 8 = 28 байт на объект
- Intersection - 4 + 4 + 16 = 24 байт на объект
- Image - 4 + 34 + 66 = 104 байт на объект
Также необходимо учесть связи между узлами:
- User-CREATED->Route - 4 + 4 + 4 = 12 байт на объект
- Walk-CREATED_BY->User - 4 + 4 + 4 = 12 байт на объект
- Walk_ALONG_THE->Route - 4 + 4 + 4 = 12 байт на объект
- Walk-CONSISTS_OF->Dot - 4 + 4 + 4 = 12 байт на объект
- Route-INCLUDE->PointOfInterest - 4 + 4 + 4 + 4 (order: Integer 4 байт) = 16 байт на объект
- PointOfInterest-CLOSE_TO_THE->Intersection - 4 + 4 + 4 = 12 байт на объект
- PointOfInterest-DEPICTED_ON->Image - 4 + 4 + 4 = 12 байт на объект
- Intersection-ROAD_SEGMENT->Intersection - 4 + 4 + 4 + 4 (length: Float 4 байт) = 16 байт на объект
В конечной формуле оценки будем отталкиваться от количества пользователей со следующими предположениями:
- в среднем на человека приходится 15 маршрутов
- в среднем маршрут состоит из 12 точек интереса
- в среднем 30000 точек интереса
- в среднем каждый пользовател прошёл 30 маршрутов
- в среднем на точку интереса приходится 9 фотографий и 9 связей DEPICTED_ON
- в среднем на отслеживание одной прогулки по маршруту требуется 35 точек отслеживания
- для количества пересечений и сегментов дорог возьмём данные по Аптекарскому острову и умножим на 30 (1842 и 9978). Это в итоге 55260 пересечений и 299340 сегментов дорог
В итоге получаем следующую зависимость объема данных в байтах от количества пользователей:
x ⋅ (175 + 15 ⋅ (1388 + 12) + 15 ⋅ 12 ⋅ 12 + 30 ⋅ (12 + 12 + 24) + 30 ⋅ 35 ⋅ (28 + 12)) + 30000 ⋅ (1372 + 9 ⋅ (104 + 12)) + 55260 ⋅ 24 + 299340 ⋅ 16 =66775x + 78595680
Следующие данные можно отнести к избыточным:
- Связь User-CREATED->Route - 12 байт
- Walk-CREATED_BY->User - 12 байт
- Walk_ALONG_THE->Route - 12 байт
- Walk-CONSISTS_OF->Dot - 12 байт
- Route-INCLUDE->PointOfInterest - 12 байт (все поля кроме order)
- PointOfInterest-CLOSE_TO_THE->Intersection - 12 байт
- PointOfInterest-DEPICTED_ON->Image - 12 байт
- Intersection-ROAD_SEGMENT->Intersection - 12 байт (все поля кроме length)
- Walk - 20 байт
- Intersection - атрибут street_count - 4 бйта
- RoadSegment - атрибут length - 4 байт
В итоге получаем излишки x⋅(15 ⋅ 12 + 15 ⋅ 12 ⋅ 12 + 30 ⋅ (12 + 12 + 20 + 12)) + 30000 ⋅ 9 ⋅ 12 + 55260 ⋅ 4 + 299340 ⋅ 4 = 4020x + 4658400
Объём чистых данных соответственно 66775x + 78835680 − 4020x - 4658400 = 62755x + 74177280
В итоге получаем следующее соотношение
- При добавлении пользователь не возникает дополнительных сущностей. Рост линейный (175 байт на объект)
- При добавлении маршрута возникает связь CREATED с пользователем, связт INCLUDE с точками интереса (среднем это 12 связей). Рост линейный (1416 байт на объект)
- При добавлении точки интереса необходимо добавить его фотографии и добавить связи DEPICTED_ON (В среднем это 9 фото). Также требуется сопоставить ближайший перекресток, т.е. добавить связь CLOSE_TO_THE. Рост линейный (1404 байт на объект)
- При добавлении нового изображения к точке интереса также возникает одна связь DEPICTED_ON. Рост линейный (116 байт на объект)
- При отслеживании нового маршрута возникает новые точки отслеживания. В среднем 35, также добавляются связи ALONG_THE, CONSISTS_OF, CREATED_BY. Рост линейный (1444 байт на объект)
- При добавлении точки OSM необходимо добавить новые сегменты дорог. В среднем на одну точку приходится 5 сегментов. Рост линейный (104 байт на объект)
В итоги для модели наблюдается только линейный рост при увеличении количества объектов.
{
"identity": 13,
"labels": [
"User"
],
"properties": {
"password": "12345678",
"name": "Иван Рур",
"created_at": "2024-10-30T18:46:51.927000000Z",
"email": "[email protected]"
},
"elementId": "4:8029c3ee-3cb0-4f9f-9549-21a0282727a1:13"
}
{
"identity": 16,
"labels": [
"Route"
],
"properties": {
"durarion": 130,
"length": 13.5,
"name": "По следам Пушкина",
"created_at": "2024-10-30T18:59:28.346000000Z",
"description": "Лучший маршрут, связанный с Пушкиным"
},
"elementId": "4:8029c3ee-3cb0-4f9f-9549-21a0282727a1:16"
}
{
"identity": 17,
"labels": [
"PointOfInterest"
],
"properties": {
"name": "Петропавловская крепость",
"created_at": "2024-10-30T19:09:06.678000000Z",
"description": "Первая крепость города",
"location": "point({srid:4326, x:56.21, y:13.43})"
},
"elementId": "4:8029c3ee-3cb0-4f9f-9549-21a0282727a1:17"
}
{
"identity": 18,
"labels": [
"Image"
],
"properties": {
"name": "default image",
"uri": "[https://uri_1](https://uri_1/)"
},
"elementId": "4:8029c3ee-3cb0-4f9f-9549-21a0282727a1:18"
}
{
"identity": 19,
"labels": [
"Walk"
],
"properties": {
"length": 0.3,
"end_time": "2024-10-30T19:21:47.217000000Z",
"start_date": "2024-10-30T00:00:00Z"
},
"elementId": "4:8029c3ee-3cb0-4f9f-9549-21a0282727a1:19"
}
{
"identity": 20,
"labels": [
"Dot"
],
"properties": {
"location": "point({srid:4326, x:-34.2, y:32.43})",
"time": "2024-10-30T19:26:11.220000000Z"
},
"elementId": "4:8029c3ee-3cb0-4f9f-9549-21a0282727a1:20"
}
{
"identity": 7,
"start": 19,
"end": 13,
"type": "CREATED_BY",
"properties": {
},
"elementId": "5:8029c3ee-3cb0-4f9f-9549-21a0282727a1:7",
"startNodeElementId": "4:8029c3ee-3cb0-4f9f-9549-21a0282727a1:19",
"endNodeElementId": "4:8029c3ee-3cb0-4f9f-9549-21a0282727a1:13"
}
{
"identity": 21,
"labels": [
"Intersection"
],
"properties": {
"location": "point({srid:4326, x:-34.2, y:32.43})",
"street_count": 4
},
"elementId": "4:8029c3ee-3cb0-4f9f-9549-21a0282727a1:21"
}
{
"identity": 8,
"start": 21,
"end": 22,
"type": "ROAD_SEGMENT",
"properties": {
"length": 0.2
},
"elementId": "5:8029c3ee-3cb0-4f9f-9549-21a0282727a1:8",
"startNodeElementId": "4:8029c3ee-3cb0-4f9f-9549-21a0282727a1:21",
"endNodeElementId": "4:8029c3ee-3cb0-4f9f-9549-21a0282727a1:22"
}
MATCH (u: User) WHERE u.email="[email protected]"
CREATE (u: User {
email: '[email protected]',
name: 'Иван Рур',
password: '12345678',
created_at: datetime()
})
Итого: 1 запрос для проверки существует ли пользователь с таким email; 1 запрос для создания пользователя
Затрагивается только одна коллекция
MATCH (u: User) WHERE u.email="[email protected]" AND u.password="12345678"
Итого: 1 запрос.
Затрагивается только одна коллекция.
MATCH (source:Intersection)
WHERE ID(source) = 45
MATCH (dest:Intersection)
WHERE ID(dest) = 14
CALL apoc.algo.aStarConfig(source, dest, "ROAD_SEGMENT",
{pointPropName: "location", weight: "length"})
YIELD weight, path
RETURN *
Итого: 3 запроса Затронуты две коллекции
MATCH (p: PointOfInterest) WHERE n.name =~ '(?i).*Петропавлав.*' RETURN p;
Итого: 1 запрос.
Затрагивается только одна коллекция.
CALL () {
MATCH
(r:Route WHERE ID(r)=8)-[inc1: INCLUDE]->
(: PointOfInterest)-[:CLOSE_TO_THE]->(i1: Intersection),
(r:Route WHERE ID(r)=8)-[inc2: INCLUDE{order: inc1.order + 1}]->
(: PointOfInterest)-[:CLOSE_TO_THE]->(i2: Intersection)
RETURN i1, i2
ORDER BY inc1.order
}
CALL apoc.algo.aStarConfig(i1, i2, "ROAD_SEGMENT", {pointPropName: "location", weight: "length"})
YIELD weight
RETURN SUM(weight) as total_distance
Итого: 2 запроса.
Затрагивается 6 коллекций.
MATCH (route: Route WHERE ID(route)=8)
CALL () {
MATCH
(route)-[inc1: INCLUDE]->
(: PointOfInterest)-[:CLOSE_TO_THE]->(i1: Intersection),
(route)-[inc2: INCLUDE{order: inc1.order + 1}]->
(: PointOfInterest)-[:CLOSE_TO_THE]->(i2: Intersection)
RETURN i1, i2
ORDER BY inc1.order
}
WITH route, i1, i2
CALL apoc.algo.aStarConfig(i1, i2, "ROAD_SEGMENT", {pointPropName: "location", weight: "length"})
YIELD weight
WITH SUM(weight) as total_distance, route
SET route.length=total_distance
RETURN route
Итого: 3 запроса.
Затрагивается 5 коллекций.
MATCH (w: Walk)-[:ALONG_THE]->(r:Route WHERE ID(r)=8) WITH r,
CASE COUNT(w)
WHEN >= 10 THEN AVG(w.duration)
ELSE r.length / 78
END AS duration
SET r.duration = duration
RETURN r
Итого: 1 запрос.
Затрагивается 3 коллекции.
CALL () {
MATCH (w :Walk WHERE ID(w)=87)-[:CONSISTS_OF]->(d :Dot),
(i: Intersection WHERE point.distance(i.location, d.location) < 10000000)
WITH d, i ORDER BY point.distance(i.location, d.location)
RETURN d, collect(i)[0] AS nearest_i
ORDER BY d.time
}
WITH collect(DISTINCT nearest_i)[0..-1] AS i1, collect(DISTINCT nearest_i)[1..] AS i2
UNWIND i1 AS s
UNWIND i2 AS d
CALL apoc.algo.aStarConfig(s, d, "ROAD_SEGMENT", {pointPropName: "location", weight: "length"})
YIELD weight
RETURN SUM(weight) AS length
Итого: 2 запроса.
Затрагивается 5 коллекций.
CALL () {
MATCH (w :Walk WHERE ID(w)=87)-[:CONSISTS_OF]->(d :Dot),
(i: Intersection WHERE point.distance(i.location, d.location) < 10000000)
WITH w, d, i ORDER BY point.distance(i.location, d.location)
RETURN w, d, collect(i)[0] AS nearest_i
ORDER BY d.time
}
WITH collect(DISTINCT nearest_i)[0..-1] AS i1, collect(DISTINCT nearest_i)[1..] AS i2, w
UNWIND i1 AS s
UNWIND i2 AS d
CALL apoc.algo.aStarConfig(s, d, "ROAD_SEGMENT", {pointPropName: "location", weight: "length"})
YIELD weight
WITH SUM(weight) AS total_distance, w
SET w.length = total_distance
RETURN w
Итого: 2 запроса.
Затрагивается 5 коллекций.
MATCH (n :Walk WHERE ID(n) = 87)-[:CONSISTS_OF]->(d: Dot)
WITH min(d.time) as start_time, max(d.time) as finish_time
RETURN duration.inSeconds(start_time, finish_time).minutes
Итого: 1 запрос.
Затрагивается 3 коллекции.
MATCH (n :Walk WHERE ID(n) = 87)-[:CONSISTS_OF]->(d: Dot)
WITH min(d.time) as start_time, max(d.time) as end_time, n
SET n.start_time=start_time,
n.end_time=end_time
RETURN n
Итого: 1 запрос.
Затрагивается 3 коллекции.
MATCH (user :User)-[:CREATED_BY]-(w :Walk)
WHERE duration.inSeconds(w.start_time, w.end_time).hours > 1
RETURN user, SUM(w.length) / SUM(duration.inSeconds(w.start_time, w.end_time).seconds) AS speed
ORDER BY speed DESC
LIMIT 20
Итого: 1 запрос.
Затрагивается 3 коллекции.
MATCH (d: Dot WHERE duration.between(d.time, Datetime()).months <= 1),
(i :Intersection WHERE point.distance(d.location, i.location) < 50)
RETURN i, COUNT(d) as popularity
ORDER BY popularity DESC
LIMIT 100
Итого: 1 запрос.
Затрагивается 2 коллекции.
MATCH (d: Dot WHERE duration.between(d.time, Datetime()).months <= 1),
(i1 :Intersection WHERE point.distance(d.location, i1.location) < 50)
-[r :ROAD_SEGMENT]-
(i2 :Intersection WHERE point.distance(d.location, i2.location) < 50)
RETURN r, COUNT(d) as popularity
ORDER BY popularity DESC
LIMIT 100
Итого: 1 запрос.
Затрагивается 2 коллекции.
CALL () {
WITH point({latitude: 51.3451242, longitude: 11.4355232}) AS source
MATCH (source_i: Intersection WHERE point.distance(source_i.location, source) < 10000)
RETURN source_i
ORDER BY point.distance(source_i.location, source)
LIMIT 1
}
CALL () {
WITH point({latitude: 52.3145351, longitude: 13.4341564}) AS dest
MATCH(dest_i: Intersection WHERE point.distance(dest_i.location, dest) < 10000)
RETURN dest_i
ORDER BY point.distance(dest_i.location, dest)
LIMIT 1
}
CALL apoc.algo.aStarConfig(source_i, dest_i, "ROAD_SEGMENT", {pointPropName: "location", weight: "length"})
YIELD path
RETURN path
Итого: 3 запроса.
Затрагивается 2 коллекции.
Хранение данных о пользователе системы
- user_id - целочисленный идентификатор (serial, 4 байта)
- name - название (varchar(50), до 50 байтов)
- email - адрес электронной почты (varchar(129), до 129 байтов)
- password - пароль (varchar(64) - до 64 байтов)
- created_at - дата регистрации (date - 4 байта)
Хранение данных о созданном пользователем маршруте
- route_id - целочисленный идентификатор (serial, 4 байта)
- name - название (varchar(50), до 50 байтов)
- user_id - целочисленный идентификатор создателя (integer, 4 байта)
- description - описание (varchar(2000), до 2 кб)
- point_num - количество точек интереса (intager, 4 байта)
- length - длина в метрах (intager, 4 байта)
- duration - продолжительность в минутах (intager, 4 байта)
- created_at - дата создания (date, 4 байта)
Хранение точек интереса на карте
- point_id - целочисленный идентификатор (serial, 4 байта)
- name - название (varchar(50), до 50 байтов)
- location - местоположение (point, 16 байтов)
- description - описание (varchar(1000), до 2 кб)
- intersection_id - идентификатор OSM точки (bigint, 8 байтов)
Хранение данных о прогулке пользователя по маршруту
- walk_id - целочисленный идентификатор (serial, 4 байта)
- user_id - идентификатор пользователя (integer, 4 байта)
- route_id - идентификатор маршрута (integer, 4 байта)
- start_datetime - дата и время начала (timestamp, 8 байтов)
- end_datetime - дата и время окончания (timestamp, 8 байтов)
- dot_count - количество точек отслеживания (integer, 4 байта)
- length - длина в метрах (integer, 4 байта)
Хранение промежуточных положений пользователя в ходе прогулки
- dot_id - целочисленный идентификатор (serial, 4 байта)
- walk_id - идентификатор прогулки (integer, 4 байта)
- location - местоположение (point, 16 байтов)
- datetime - дата и время (timestamp, 8 байтов)
Хранение данных о местоположении перекрестков на карте
- intersection_id - целочисленный идентификатор точки OSM (bigint, 8 байтов)
- street_count - количество улиц (integer, 4 байта)
- location - местоположение (point, 16 байтов)
Хранение сегментов дорог, соединяющих перекрестки
- roadseg_id - целочисленный идентификатор (serial, 4 байта)
- from_intersection_id - идентификатор OSM начальной точки (bigint, 8 байтов)
- in_intersection_id - идентификатор OSM конечной точки (bigint, 8 байтов)
- length - длина сегмента в метрах (integer, 4 байта)
Хранение ссылки на загруженные в систему изображения
- image_id - целочисленный идентификатор (serial, 4 байта)
- name - название изображения (varchar(50), до 50 байтов)
- uri - uri изображения (varchar(50), до 100 байтов)
Возьмём средний объём текста в полях без чёткого объёма как 2/3 с округлением в верх от максимального. Аналогичное сделано и при оценке для нереляционной модели.
- User -
$4+34+86+43+4=171$ байт на объект - Route -
$4+34+4+1334+4+4+4+4=1392$ байт на объект - PointOfInterest -
$4+34+16+1334+8+4=1400$ байт на объект - Walk -
$4+4+4+8+8+4+4=36$ байт на объект - Dot -
$4+4+16+8=32$ байт на объект - Intersection -
$8+4+16=28$ байт на объект - RoadSegment -
$4+8+8+4=24$ байт на объект - Image -
$4+34+67=105$ байт на объект
Также связь между рядом отношений поражает другие отношения:
- Route-PointOfInterest -
$4+4+4=12$ байт на связь (третье поле - нумерация для установления порядка) - PointOfInterest-Image -
$4+4=8$ байт на связь
В конечной формуле оценки будем отталкиваться от количества пользователей со следующими предположениями:
- в среднем на человека приходится 15 маршрутов
- в среднем маршрут состоит из 12 точек интереса
- в среднем каждый клиент прошёл 30 маршрутов
- в среднем на отслеживание одной прогулки по маршруту требуется 35 точек отслеживания
- для количества пересечений и сегментов дорог возьмём данные по Аптекарскому острову и умножим на 25 (4519 и 20720). Это в итоге 112975 пересечений и 518000 сегментов дорог
- точек интереса 30000
- в среднем на точку интереса приходится 9 фотографий
В итоге получаем
Следующие можно посчитать избыточностью:
- Route - атрибут user_id, point_num, length, duration - 16 байт на объект
- PointOfInterest - атрибут intersection_id - 8 байт на объект
- Walk - атрибуты user_id, route_id, start_datetime, end_datetime, dot_count и length - 32 байт на объект
- Dot - атрибут walk_id - 4 байта на объект
- RoadSegment - атрибуты from_intersection, in_intersection, length - 20 байт
- Intersection - атрибут street_count - 4 байта
- Route-PointOfIntest - атрибуты route_id и point_id - 8 байт
- PointOfIntest-Image полностью - 8 байт
В итоге получаем излишки
Объём чистых данных соответственно
В итоге получаем следующее соотношение
- При добавлении пользователь не возникает дополнительных сущностей. Рост линейный (171 байт на объект)
- При добавлении маршрута возникает новое количество связей между маршрутом и точкой интереса. В среднем это 12 связей. Рост линейный (1536 байт на объект)
- При добавлении точки интереса необходимо добавить его фотографии и связать их с ним. В среднем это 9 фото. Рост линейный (2417 байт на объект)
- При добавлении нового изображения к точке интереса также возникает одно дополнительная связь. Рост линейный (113 байт на объект)
- При отслеживании нового маршрута возникает новые точки отслеживания. В среднем 35. Рост линейный (1156 байт на объект)
- При добавлении точки OSM необходимо добавить новые сегменты дорог. В среднем на одну точку приходится 5 сегментов. Рост линейный (148 байт на объект)
В итоге для модели наблюдается только линейный рост при увеличении количества объектов.
user_id | name | password | created_at | |
---|---|---|---|---|
1 | Иван Рур | [email protected] | 12345678 | 2023-01-01 |
2 | Саша Саша | [email protected] | 87654321 | 2023-01-02 |
route_id | name | user_id | description | point_num | length | duration | created_at |
---|---|---|---|---|---|---|---|
1 | По следам Пушкина | 1 | Лучший маршрут связный с Пушкиным | 2 | 2001 | 130 | 02.02.2023 |
2 | Петроградка | 2 | Маршрут по Петроградском острову | 2 | 1000 | 67 | 03.02.2023 |
point_id | name | location | description | intersection_id | created_at |
---|---|---|---|---|---|
1 | Петропавловская крепость | point(x:30.3024903, y:59.9713101) | Первая крепость города | 1234 | 01.01.2023 |
2 | Памятник Пушкину | point(x:30.2924903, y:59.9113101) | Отлит из бронзы | 2341 | 01.01.2023 |
3 | Аврора | point(x:20.9924903, y:60.0013101) | Корабль революции | 3412 | 01.01.2023 |
4 | Место дуэли Пушкина | point(x:20.8124903, y:60.3413101) | Здесь стрелялся Пушкин | 4123 | 01.01.2023 |
route_id | point_id | position |
---|---|---|
1 | 2 | 1 |
1 | 4 | 2 |
2 | 1 | 1 |
2 | 3 | 2 |
image_id | name | uri |
---|---|---|
1 | img1 | https://uri_1 |
2 | img2 | https://uri_2 |
3 | img3 | https://uri_3 |
4 | img4 | https://uri_4 |
user_id | image_id |
---|---|
1 | 1 |
2 | 2 |
3 | 3 |
4 | 4 |
walk_id | user_id | route_id | start_datetime | end_datetime | dot_count | length |
---|---|---|---|---|---|---|
1 | 1 | 2 | 2023-01-01 00:00:00 | 2023-01-01 01:07:00 | 2 | 1002 |
2 | 2 | 1 | 2023-01-01 00:00:00 | 2023-01-01 02:10:00 | 2 | 2000 |
dot_id | walk_id | location | time |
---|---|---|---|
1 | 1 | point(x:30.3024903, y:59.9713101) | 2023-01-01 00:00:00 |
2 | 2 | point(x:30.2924903, y:59.9113101) | 2023-01-01 00:00:00 |
3 | 1 | point(x:20.9924903, y:60.0013101) | 2023-01-01 01:07:00 |
4 | 2 | point(x:20.8124903, y:60.3413101) | 2023-01-01 02:10:00 |
intersection_id | street_count | location |
---|---|---|
1234 | 1 | point(x:30.3024903, y:59.9713101) |
2341 | 2 | point(x:30.2924903, y:59.9113101) |
3412 | 2 | point(x:20.9924903, y:60.0013101) |
4123 | 1 | point(x:20.8124903, y:60.3413101) |
roadseg_id | from_intersection_id | in_intersection_id | length |
---|---|---|---|
1 | 1234 | 2341 | 345 |
2 | 2341 | 3412 | 354 |
3 | 3412 | 4123 | 376 |
SELECT * FROM User WHERE email = '[email protected]';
INSERT INTO User (email, name, password, created_at)
VALUES ('[email protected]', 'Иван Рур', '12345678', CURRENT_DATE);
Итого: 2 запроса.
Затронута 1 коллекция
SELECT * FROM User
WHERE email = '[email protected]' AND password = '12345678';
Итого: 1 запрос.
Затронута 1 коллекция
SELECT * FROM PointOfIntest
WHERE LOWER(name) ILIKE 'аврора' OR LOWER(description) ILIKE 'аврора';
Итого: 1 запрос.
Затронута 1 коллекция
pgr_astar('SELECT
rs.roadseg_id AS source,
rs.in_intersection_id AS target,
rs.length AS cost,
i1.location[0] AS x1,
i1.location[1] AS y1,
i2.location[0] AS x2,
i2.location[1] AS y2
FROM
RoadSegment rs
JOIN Intersection i1 ON rs.from_intersection_id = i1.intersection_id
JOIN Intersection i2 ON rs.in_intersection_id = i2.intersection_id
', start, end, false, false)
Итого: 3 запроса.
Затронуто 3 коллекции
WITH RouteInPoints AS (
SELECT intersection_id, position
FROM RoutePoint
JOIN PointOfInterest ON PointOfInterest.point_id = RoutePoint.point_id
WHERE route_id = 1
)
SELECT SUM(cost) AS total_distance
FROM (
SELECT astar_result.cost,
FROM RouteInPoints rp1
JOIN
RouteInPoints rp2 ON rp1.position + 1 = rp2.position,
pgr_astar('SELECT
rs.roadseg_id AS source,
rs.in_intersection_id AS target,
rs.length AS cost,
i1.location[0] AS x1,
i1.location[1] AS y1,
i2.location[0] AS x2,
i2.location[1] AS y2
FROM
RoadSegment rs
JOIN Intersection i1 ON rs.from_intersection_id = i1.intersection_id
JOIN Intersection i2 ON rs.in_intersection_id = i2.intersection_id
', rp1.intersection_id, rp2.intersection_id, false, false) AS astar_result;
) AS distance;
Итого: 3 запроса.
Затронуты 3 коллекции
UPDATE Route
SET length = (
WITH RouteInPoints AS (
SELECT intersection_id, position
FROM RoutePoint
JOIN PointOfInterest ON PointOfInterest.point_id = RoutePoint.point_id
WHERE route_id = 1
)
SELECT SUM(cost) AS total_distance
FROM (
SELECT astar_result.cost,
FROM RouteInPoints rp1
JOIN
RouteInPoints rp2 ON rp1.position + 1 = rp2.position,
pgr_astar('SELECT
rs.roadseg_id AS source,
rs.in_intersection_id AS target,
rs.length AS cost,
i1.location[0] AS x1,
i1.location[1] AS y1,
i2.location[0] AS x2,
i2.location[1] AS y2
FROM
RoadSegment rs
JOIN Intersection i1 ON rs.from_intersection_id = i1.intersection_id
JOIN Intersection i2 ON rs.in_intersection_id = i2.intersection_id',
rp1.intersection_id, rp2.intersection_id, false, false
) AS astar_result
) AS distance
)
WHERE route_id = 1;
Итого: 4 запроса.
Затронуты 4 коллекции
CASE WHEN (SELECT COUNT(*) FROM Walk WHERE route_id = 1) > 10 THEN
UPDATE Route SET
duration =
(
SELECT ROUND(AVG(EXTRACT(EPOCH FROM (end_time - start_time)) / 60 / (Walk.length / Route.length)))
FROM Walk
JOIN Route ON Walk.route_id = Route.route_id
WHERE route_id = 1
)
WHERE route_id = 1
ELSE
UPDATE Route SET
duration = length / 78
-- 78 меторв в минуту
WHERE route_id = 1
END
Итого: 3 запроса.
Затронуты 2 коллекции
Update Walk SET
length =
(
WITH ordered_dots AS (
SELECT dot_id, location, time,
LAG(location) OVER (PARTITION BY walk_id ORDER BY time) AS prev_location
FROM Dot
WHERE walk_id = 1
)
SELECT SUM(location <@> prev_location)
FROM ordered_dots
WHERE prev_location IS NOT NULL
)
WHERE walk_id = 1;
Итого: 3 запроса.
Затронуты 2 коллекции
-- SELECT EXTRACT(EPOCH FROM (MAX(time) - MIN(time))) / 60 AS duration_in_minutes
-- FROM Dot
-- WHERE walk_id = 1;
UPDATE Walk SET
start_time = (SELECT MIN(time) FROM Dot WHERE walk_id = 1),
end_time = (SELECT MAX(time) FROM Dot WHERE walk_id = 1)
WHERE walk_id = 1;
Итого: 3 запроса.
Затронуты 2 коллекции
SELECT user_id, SUM(Walk.length) / SUM(Walk.duration) AS speed
FROM User
JOIN Walk ON User.user_id = Walk.user_id
GROUP BY user_id
HAVING SUM(Walk.duration) > 60
ORDER BY speed DESC;
Итого: 1 запрос.
Затронуты 2 коллекции
SELECT i.intersection_id, SUM(151 - d.location <@> i.location) AS popularity
FROM Intersection i
JOIN Dot d ON d.location <@> i.location < 150 AND d.time > NOW() - INTERVAL '1 month'
GROUP BY i.intersection_id
ORDER BY popularity DESC
LIMIT 100;
Итого: 1 запроса.
Затронуты 2 коллекции
SELECT rs.roadseg_id, SUM(151 - d.location <@> i1.location) + SUM(151 - d.location <@> i1.location) AS popularity
FROM RoadSegment rs
JOIN Intersection i1 ON rs.from_intersection_id = i1.intersection_id
JOIN Intersection i2 ON rs.in_intersection_id = i2.intersection_id
JOIN Dot d ON (d.location <@> i1.location < 150 OR d.location <@> i2.location < 150) AND d.time > NOW() - INTERVAL '1 month'
GROUP BY rs.roadseg_id
ORDER BY popularity DESC
LIMIT 100;
Итого: 1 запрос.
Затронуты 3 коллекции
pgr_astar('SELECT
rs.roadseg_id AS source,
rs.in_intersection_id AS target,
rs.length AS cost,
i1.location[0] AS x1,
i1.location[1] AS y1,
i2.location[0] AS x2,
i2.location[1] AS y2
FROM
RoadSegment rs
JOIN Intersection i1 ON rs.from_intersection_id = i1.intersection_id
JOIN Intersection i2 ON rs.in_intersection_id = i2.intersection_id
',
(
SELECT intersection_id
FROM Intersection
ORDER BY location <@> sta_point
LIMIT 1;
)
,
(
SELECT intersection_id
FROM Intersection
ORDER BY location <@> end_point
LIMIT 1;
)
, false, false);
Итого: 3 запроса.
Затронуты 2 коллекции
Реляционная модель демонстрирует больший удельный объём информации. При этом нереляционная модель имеет меньшую избыточность информации.
Описываемый объект | Neo4j | SQL |
---|---|---|
User | 175 байт | 171 байт |
Route | 1388 байт | 1392 байта |
PointOfInterest | 1372 байта | 1400 байт |
Walk | 24 байта | 36 байт |
Dot | 28 байт | 32 байта |
Intersection | 24 байта | 28 байт |
Image | 104 байта | 105 байт |
Избыточность | 1.063 | 1.159 |
Use Case | Neo4j | SQL |
---|---|---|
Регистрация | 2 операции, 1 коллекция | 2 операции, 1 коллекция |
Вход | 1 операция, 1 коллекция | 1 операция, 1 коллекция |
Поиск Точек | 1 операция, 1 коллекция | 1 операция, 1 коллекция |
Поиск кратчайшего пути между точками | 3 операции, 2 коллекции | 3 операции, 3 коллекции |
Расчет длины маршрута | 2 операции, 6 коллекций | 3 операции, 3 коллекции |
Обновление длины маршрута | 3 операции, 5 коллекций | 4 операции, 4 коллекции |
Обновление длительности маршрута | 1 операция, 3 коллекции | 3 операции, 2 коллекции |
Обновление длины Walk | 2 операции, 5 коллекций | 3 операции, 2 коллекции |
Обновление длительности прогулки | 1 операция, 3 коллекции | 3 операции, 2 коллекции |
20 самых быстрых пользователей | 1 операция, 3 коллекции | 1 операция, 2 коллекции |
100 самых популярных intersection | 1 операция, 2 коллекции | 1 операция, 2 коллекции |
100 самых популярных RoadSegment | 1 операция, 2 коллекции | 1 операция, 3 коллекции |
Построение маршрута между двумя произвольными точками | 3 операции, 2 коллекции | 3 операции, 2 коллекции |
Нереляционная модель требует в среднем меньшее количество операций, чем реляционная модель, но запросы затрагивают большее количество коллекций из-за использования связей между узлами.
В отличие от реляционной, нереляционная модель данных лучше масштабируется. Модели данных с множественными связями (например, Route и PointOfInterest) удобнее и эффективнее моделировать при помощи графов и базирующихся на них базах данных, таких как, например, neo4j. Реляционная модель лучше справляется с линейными структурами и стандартными операциями, такими как фильтрация данных и операции с большими таблицами, но может быть менее удобна для сложных связей между сущностями. Выбор между SQL и noSQL базами данных зависит от особенностей данных и задач.