diff --git a/packages/database/features/addContent.feature b/packages/database/features/addContent.feature index 9148b6328..19c025349 100644 --- a/packages/database/features/addContent.feature +++ b/packages/database/features/addContent.feature @@ -19,8 +19,7 @@ Feature: Content access "source_local_id": "page1_uid", "created": "2000/01/01", "last_modified": "2001/01/02", - "author_local_id": "user1", - "content_type": "text/roam+markdown" + "author_local_id": "user1" } ] """ @@ -37,15 +36,13 @@ Feature: Content access "space_local_id": "s1", "created": "2000/01/01", "last_modified": "2001/01/02", - "author_local_id": "user2", - "content_type": "text/roam+markdown" + "author_local_id": "user2" }, "source_local_id": "s1", "scale": "document", "created": "2000/01/01", "last_modified": "2001/01/02", - "text": "Some text", - "content_type": "text/plain" + "text": "Some text" }, { "author_local_id": "user2", @@ -56,8 +53,7 @@ Feature: Content access "created": "2000/01/02", "last_modified": "2001/01/03", "part_of_local_id": "s1", - "text": "Some subtext", - "content_type": "text/plain" + "text": "Some subtext" }, { "space_local_id": "s1", @@ -67,8 +63,7 @@ Feature: Content access "space_local_id": "s1", "created": "2000/01/01", "last_modified": "2001/01/02", - "author_local_id": "user2", - "content_type": "text/roam+markdown" + "author_local_id": "user2" }, "source_local_id": "s3", "scale": "document", @@ -76,7 +71,6 @@ Feature: Content access "last_modified": "2001/01/03", "part_of_local_id": "s2", "text": "Some subsubtext", - "content_type": "text/plain", "embedding_inline": { "model": "openai_text_embedding_3_small_1536", "vector": [ diff --git a/packages/database/supabase/migrations/20260713133517_accept_empty_content_type.sql b/packages/database/supabase/migrations/20260713133517_accept_empty_content_type.sql new file mode 100644 index 000000000..96b5c86f0 --- /dev/null +++ b/packages/database/supabase/migrations/20260713133517_accept_empty_content_type.sql @@ -0,0 +1,144 @@ +CREATE OR REPLACE FUNCTION public.upsert_content(v_space_id bigint, data jsonb, v_creator_id BIGINT, content_as_document boolean DEFAULT TRUE) +RETURNS SETOF BIGINT +SET search_path = '' +LANGUAGE plpgsql +AS $$ +DECLARE + v_platform public."Platform"; + db_document public."Document"%ROWTYPE; + document_id BIGINT; + local_content public.content_local_input; + db_content public."Content"%ROWTYPE; + content_row JSONB; + upsert_id BIGINT; +BEGIN + SELECT platform INTO STRICT v_platform FROM public."Space" WHERE id=v_space_id; + FOR content_row IN SELECT * FROM jsonb_array_elements(data) + LOOP + local_content := jsonb_populate_record(NULL::public.content_local_input, content_row); + local_content.space_id := v_space_id; + IF content_type(local_content) IS NULL THEN + local_content.content_type := CASE + WHEN variant(local_content)!='full' THEN 'text/plain' + WHEN v_platform='Roam' THEN 'text/roam+markdown' + WHEN v_platform='Obsidian' THEN 'text/obsidian+markdown' + ELSE 'text/plain' END; + END IF; + db_content := public._local_content_to_db_content(local_content); + IF account_local_id(author_inline(local_content)) IS NOT NULL THEN + SELECT public.create_account_in_space( + v_space_id, + account_local_id(author_inline(local_content)), + name(author_inline(local_content)) + ) INTO STRICT upsert_id; + db_content.author_id := upsert_id; + END IF; + IF account_local_id(creator_inline(local_content)) IS NOT NULL THEN + SELECT public.create_account_in_space( + v_space_id, + account_local_id(creator_inline(local_content)), + name(creator_inline(local_content)) + ) INTO STRICT upsert_id; + db_content.creator_id := upsert_id; + END IF; + IF content_as_document THEN + db_content.scale = 'document'; + END IF; + IF content_as_document AND document_id(db_content) IS NULL AND source_local_id(document_inline(local_content)) IS NULL THEN + local_content.document_inline.space_id := v_space_id; + local_content.document_inline.source_local_id := db_content.source_local_id; + local_content.document_inline.last_modified := db_content.last_modified; + local_content.document_inline.created := db_content.created; + local_content.document_inline.author_id := db_content.author_id; + END IF; + IF source_local_id(document_inline(local_content)) IS NOT NULL THEN + IF content_type(document_inline(local_content)) IS NULL THEN + local_content.document_inline.content_type := CASE + WHEN v_platform='Roam' THEN 'text/roam+markdown' + WHEN v_platform='Obsidian' THEN 'text/obsidian+markdown' + ELSE 'text/plain' END; + END IF; + db_document := public._local_document_to_db_document(document_inline(local_content)); + IF (db_document.author_id IS NULL AND author_inline(local_content) IS NOT NULL) THEN + db_document.author_id := upsert_account_in_space(v_space_id, author_inline(local_content)); + END IF; + INSERT INTO public."Document" ( + space_id, + source_local_id, + url, + created, + metadata, + last_modified, + author_id, + contents, + content_type + ) VALUES ( + COALESCE(db_document.space_id, v_space_id), + db_document.source_local_id, + db_document.url, + db_document.created, + COALESCE(db_document.metadata, '{}'::jsonb), + db_document.last_modified, + db_document.author_id, + db_document.contents, + db_document.content_type + ) + ON CONFLICT (space_id, source_local_id) DO UPDATE SET + url = COALESCE(db_document.url, EXCLUDED.url), + created = COALESCE(db_document.created, EXCLUDED.created), + metadata = COALESCE(db_document.metadata, EXCLUDED.metadata), + last_modified = COALESCE(db_document.last_modified, EXCLUDED.last_modified), + author_id = COALESCE(db_document.author_id, EXCLUDED.author_id), + contents = COALESCE(db_document.contents, EXCLUDED.contents), + content_type = COALESCE(db_document.content_type, EXCLUDED.content_type) + RETURNING id INTO STRICT document_id; + db_content.document_id := document_id; + END IF; + INSERT INTO public."Content" ( + document_id, + source_local_id, + variant, + author_id, + creator_id, + created, + text, + metadata, + scale, + space_id, + last_modified, + part_of_id, + content_type + ) VALUES ( + db_content.document_id, + db_content.source_local_id, + COALESCE(db_content.variant, 'direct'::public."ContentVariant"), + db_content.author_id, + db_content.creator_id, + db_content.created, + db_content.text, + COALESCE(db_content.metadata, '{}'::jsonb), + db_content.scale, + db_content.space_id, + db_content.last_modified, + db_content.part_of_id, + db_content.content_type + ) + ON CONFLICT (space_id, source_local_id, variant) DO UPDATE SET + document_id = COALESCE(db_content.document_id, EXCLUDED.document_id), + author_id = COALESCE(db_content.author_id, EXCLUDED.author_id), + creator_id = COALESCE(db_content.creator_id, EXCLUDED.creator_id), + created = COALESCE(db_content.created, EXCLUDED.created), + text = COALESCE(db_content.text, EXCLUDED.text), + metadata = COALESCE(db_content.metadata, EXCLUDED.metadata), + scale = COALESCE(db_content.scale, EXCLUDED.scale), + last_modified = COALESCE(db_content.last_modified, EXCLUDED.last_modified), + part_of_id = COALESCE(db_content.part_of_id, EXCLUDED.part_of_id), + content_type = COALESCE(db_content.content_type, EXCLUDED.content_type) + RETURNING id INTO STRICT upsert_id; + IF model(embedding_inline(local_content)) IS NOT NULL THEN + PERFORM public.upsert_content_embedding(upsert_id, model(embedding_inline(local_content)), vector(embedding_inline(local_content))); + END IF; + RETURN NEXT upsert_id; + END LOOP; +END; +$$; diff --git a/packages/database/supabase/schemas/content.sql b/packages/database/supabase/schemas/content.sql index 54f2a3bc9..b51b77544 100644 --- a/packages/database/supabase/schemas/content.sql +++ b/packages/database/supabase/schemas/content.sql @@ -545,6 +545,13 @@ BEGIN LOOP local_content := jsonb_populate_record(NULL::public.content_local_input, content_row); local_content.space_id := v_space_id; + IF content_type(local_content) IS NULL THEN + local_content.content_type := CASE + WHEN variant(local_content)!='full' THEN 'text/plain' + WHEN v_platform='Roam' THEN 'text/roam+markdown' + WHEN v_platform='Obsidian' THEN 'text/obsidian+markdown' + ELSE 'text/plain' END; + END IF; db_content := public._local_content_to_db_content(local_content); IF account_local_id(author_inline(local_content)) IS NOT NULL THEN SELECT public.create_account_in_space(