If you're looking for alternatives other than JSON, and you just need arrays of text name-value pairs collections, you could simply use PostgreSQL's arrays:
CREATE TYPE mydoc AS (content_type text, names text[], values text[]);
CREATE TABLE documentData(id int primary key, documentName text, docs mydoc[]);
INSERT INTO documentData(id,documentName,docs) VALUES (1, 'DocumentA', '{"(Table,\"{ColA,ColB,ColC}\",\"{val1,val2,val3}\")","(Table,\"{ColA,ColB,ColC}\",\"{val4,val5,val6}\")"}'::mydoc[])
SELECT d.id, d.documentName, dd.*
FROM documentData d
LEFT JOIN LATERAL unnest(d.docs) dd ON (true);
This won't get a significant advantage over JSON, other than portability to older PostgreSQL versions, but unnest
and =ANY
type of operations are often much simpler than the JSON functions, so it may be easier to query and manipulate, e.g.:
SELECT d.id, d.documentName, dd.content_type, pos, name, dd.values[pos] AS value
FROM documentData d
LEFT JOIN LATERAL unnest(d.docs) dd ON (true)
LEFT JOIN LATERAL unnest(dd.names) WITH ORDINALITY AS y (name, pos) ON (true)
WHERE dd.content_type = 'Table' AND name ~ '^Col[AB]';
Optimization
You're going down the right track.
You either need to
- Denormalize
- Cache
Caching the results
What you probably want is a MATERIALIZED VIEW
. This is easy and works reasonably well.
CREATE MATERIALIZED VIEW foo
AS
SELECT t.id, to_tsvector(concat_ws(' ',a.name, o.address, t.description, a.name)) AS tsv
FROM tasks AS t
INNER JOIN actors AS a ON t.actor_id = a.id
INNER JOIN objects AS o ON t.object_id = o.id
;
Then just
SELECT * FROM foo WHERE tsv @@ plainto_tsquery('foo bar');
Denormalizing the table
This can take a lot of forms, you've got this right though..
Redesign
Searching everything in a fuzzy fashion like this is a losing game. Even this knock off of Dungeon and Dragons meets Yahoo Answers has rules.
It becomes a lot easier to generate a query when you introduce syntax likes [text]
for tagging, and is:answer
to search just answers, rather than rebuilding Google and normalized indexes.
Best Answer
Build a string consisting of what you want to match against, and test against that. It could be as simple as casting the whole row to text:
Or, if you want to ensure that commas in the search string don't cause it to match more than one column, or if you need to omit any of the columns, use
concat()
to separate the values with unprintable chars instead.