Каков наилучший способ создания таблицы данных на основе тегов с помощью Sqlite? - PullRequest
0 голосов
/ 02 июля 2018

Json, полученный от сервера, имеет эту форму.

[
 {
  "id": 1103333,
  "name": "James",
  "tagA": [
    "apple",
    "orange",
    "grape"
  ],
  "tagB": [
    "red",
    "green",
    "blue"
  ],
  "tagC": null
  },

  {
  "id": 1103336,
  "name": "John",
  "tagA": [
    "apple",
    "pinapple",
    "melon"
  ],
  "tagB": [
    "black",
    "white",
    "blue"
  ],
  "tagC": [
    "London",
    "New York"
    ]
  }
]

Объект может иметь несколько тегов, а тег может быть связан с несколькими объектами.

В этом списке я хочу найти объект, у которого tagA - яблоко или виноград, а tagB - черный.

Это первая таблица, которую я писал.

create table response(id integer primary key, name text not null, tagA text, 
tagB text, tagC text)

select * from response where (tagA like '%apple%' or tagA like '%grape%') and (tagB like '%black%')

В этом типе таблиц есть проблема, связанная с тем, что скорость поиска очень низкая, поскольку она не поддерживает функцию поверхности функции fts при использовании библиотеки ORM, такой как Room.

Следующее, о чем я подумал, было создание таблицы для каждого тега.

create table response(id integer primary key, name text not null)

create table tagA(objectID integer, value text, primary key(objectID, value))

create table tagB(objectID integer, value text, primary key(objectID, value))

create table tagC(objectID integer, value text, primary key(objectID, value))

select * from response where id in ((select objectId from tagA where value in ('apple','grape')) 
intersect
(select objectId from tagB where value in 'black'))

Это значительно увеличивает время вставки и емкость APK (примерно вдвое больше для дополнительной таблицы), но скорость поиска значительно уступает скорости виртуальной таблицы FTS.

Я хочу избегать этого так же, как использую таблицы FTS, потому что есть больше вещей, которыми я должен управлять сам.

Есть много вещей, которые я пропустил (индекс и т. Д.), Но я не могу понять, что это такое.

Как я могу оптимизировать базу данных без использования метода FTS?

1 Ответ

0 голосов
/ 02 июля 2018

Вы можете использовать справочную таблицу (также называемую таблицей сопоставления вместе со множеством других имен), чтобы разрешить связь «многие-многие» между тегами (одна таблица для всех) и объектами (снова одна таблица).

Таким образом, у вас есть таблица objects , каждый объект имеет id , и у вас снова есть таблица tags с id для каждого объект. Так что-то вроде: -

DROP TABLE IF EXISTS object_table;
CREATE TABLE IF NOT EXISTS object_table (id INTEGER PRIMARY KEY, object_name);
DROP TABLE IF EXISTS tag_table;
CREATE TABLE IF NOT EXISTS tag_table (id INTEGER PRIMARY KEY, tag_name);

Вы бы заполнили оба, например

INSERT INTO object_table (object_name) VALUES
    ('Object1'),('Object2'),('Object3'),('Object4');
INSERT INTO tag_table (tag_name) VALUES
    ('Apple'),('Orange'),('Grape'),('Pineapple'),('Melon'),
    ('London'),('New York'),('Paris'),
    ('Red'),('Green'),('Blue'); -- and so on

Таблица сопоставления будет выглядеть примерно так: -

DROP TABLE IF EXISTS object_tag_mapping;
CREATE TABLE IF NOT EXISTS object_tag_mapping (object_reference INTEGER, tag_reference INTEGER);

Сверхурочное время, поскольку теги присваиваются объектам или наоборот, вы добавляете сопоставления, например, : -

INSERT INTO object_tag_mapping VALUES
    (1,4), -- obj1 has tag Pineapple
    (1,1),  -- obj1 has Apple
    (1,8), -- obj1 has Paris
    (1,10), -- obj1 has green
    (4,1),(4,3),(4,11), -- some tags for object 4
    (2,8),(2,7),(2,4), -- some tags for object 2
    (3,1),(3,2),(3,3),(3,4),(3,5),(3,6),(3,7),(3,8),(3,9),(3,10),(3,11); -- all tags for object 3

После этого у вас могут появиться такие запросы: -

SELECT object_name, 
    group_concat(tag_name,' ~ ') AS tags_for_this_object 
FROM object_tag_mapping 
JOIN object_table ON object_reference = object_table.id
JOIN tag_table ON tag_reference = tag_table.id
GROUP BY object_name
;
  • group_concat - агрегатная функция (применяется для GROUP), которая объединяет все значения, найденные для указанного столбца, с (необязательным) разделителем.

Результат запроса: -

enter image description here

Следующим может быть поиск, основанный на тегах (не то, чтобы вы вероятно использовали и tag_name, и tag_reference): -

SELECT object_name, tag_name 
FROM object_tag_mapping 
JOIN object_table ON object_reference = object_table.id
JOIN tag_table ON tag_reference = tag_table.id
WHERE tag_name = 'Pineapple' OR tag_reference = 9
;

Это приведет к: -

enter image description here


  • Обратите внимание, что это простой обзор, например Вы можете рассмотреть возможность использования таблицы сопоставления в качестве таблицы БЕЗ ROWID, возможно, у вас есть составное УНИКАЛЬНОЕ ограничение.

Дополнительный комментарий: -

Как реализовать запрос, содержащий два или более тега одновременно время

Это немного сложнее, если вы хотите определенные теги, но все же выполнимо. Вот пример использования CTE (Common Table Expression) вместе с предложением HAVING (предложение where применяется после генерации вывода, поэтому может применяться к агрегатам): -

WITH cte1(otm_oref,otm_tref,tt_id,tt_name, ot_id, ot_name) AS 
    (
        SELECT * FROM object_tag_mapping 
        JOIN tag_table ON tag_reference = tag_table.id 
        JOIN object_table ON object_reference = object_table.id
        WHERE tag_name = 'Pineapple' OR tag_name = 'Apple'
    )
SELECT ot_name, group_concat(tt_name), count() AS cnt FROM CTE1 
GROUP BY otm_oref
HAVING cnt = 2
;

В результате: -

enter image description here

...