[Groonga-commit] pgroonga/pgroonga at aeda0fc [master] jsonb: support empty array/object in @>

Back to archive index

Kouhei Sutou null+****@clear*****
Sun Sep 27 18:13:47 JST 2015


Kouhei Sutou	2015-09-27 18:13:47 +0900 (Sun, 27 Sep 2015)

  New Revision: aeda0fc3bc87361b766bf73980184aff578945c8
  https://github.com/pgroonga/pgroonga/commit/aeda0fc3bc87361b766bf73980184aff578945c8

  Message:
    jsonb: support empty array/object in @>

  Added files:
    expected/jsonb/contain/array/empty.out
    expected/jsonb/contain/object/empty.out
    expected/jsonb/object/bitmapscan.out
    expected/jsonb/object/indexscan.out
    sql/jsonb/contain/array/empty.sql
    sql/jsonb/contain/object/empty.sql
  Modified files:
    pgroonga.c
  Renamed files:
    expected/jsonb/contain/array/multiple-elements.out
      (from expected/jsonb/contain/array.out)
    sql/jsonb/contain/array/multiple-elements.sql
      (from sql/jsonb/contain/array.sql)

  Added: expected/jsonb/contain/array/empty.out (+28 -0) 100644
===================================================================
--- /dev/null
+++ expected/jsonb/contain/array/empty.out    2015-09-27 18:13:47 +0900 (d043a66)
@@ -0,0 +1,28 @@
+CREATE TABLE logs (
+  id int,
+  record jsonb
+);
+INSERT INTO logs VALUES (1, '[]');
+INSERT INTO logs VALUES (2, '[100]');
+INSERT INTO logs VALUES (3, '["hello"]');
+INSERT INTO logs VALUES (4, '[true]');
+INSERT INTO logs VALUES (5, '[{"object": "value"}]');
+INSERT INTO logs VALUES (6, '{"object": []}');
+CREATE INDEX pgroonga_index ON logs USING pgroonga (record);
+SET enable_seqscan = off;
+SET enable_indexscan = on;
+SET enable_bitmapscan = off;
+SELECT id, record
+  FROM logs
+ WHERE record @> '[]'::jsonb
+ ORDER BY id;
+ id |        record         
+----+-----------------------
+  1 | []
+  2 | [100]
+  3 | ["hello"]
+  4 | [true]
+  5 | [{"object": "value"}]
+(5 rows)
+
+DROP TABLE logs;

  Renamed: expected/jsonb/contain/array/multiple-elements.out (+0 -0) 100%
===================================================================

  Added: expected/jsonb/contain/object/empty.out (+28 -0) 100644
===================================================================
--- /dev/null
+++ expected/jsonb/contain/object/empty.out    2015-09-27 18:13:47 +0900 (791da8e)
@@ -0,0 +1,28 @@
+CREATE TABLE logs (
+  id int,
+  record jsonb
+);
+INSERT INTO logs VALUES (1, '{}');
+INSERT INTO logs VALUES (2, '{"key": 100}');
+INSERT INTO logs VALUES (3, '{"key": "hello"}');
+INSERT INTO logs VALUES (4, '{"key": true}');
+INSERT INTO logs VALUES (5, '{"key": []}');
+INSERT INTO logs VALUES (6, '[{}]');
+CREATE INDEX pgroonga_index ON logs USING pgroonga (record);
+SET enable_seqscan = off;
+SET enable_indexscan = on;
+SET enable_bitmapscan = off;
+SELECT id, record
+  FROM logs
+ WHERE record @> '{}'::jsonb
+ ORDER BY id;
+ id |      record      
+----+------------------
+  1 | {}
+  2 | {"key": 100}
+  3 | {"key": "hello"}
+  4 | {"key": true}
+  5 | {"key": []}
+(5 rows)
+
+DROP TABLE logs;

  Added: expected/jsonb/object/bitmapscan.out (+21 -0) 100644
===================================================================
--- /dev/null
+++ expected/jsonb/object/bitmapscan.out    2015-09-27 18:13:47 +0900 (be7011e)
@@ -0,0 +1,21 @@
+CREATE TABLE logs (
+  id int,
+  record jsonb
+);
+INSERT INTO logs VALUES (1, '{"message": {"code": 100, "content": "hello"}}');
+INSERT INTO logs VALUES (1, '{"message": "hello"}');
+INSERT INTO logs VALUES (1, '{"message": ["hello", "world"]}');
+CREATE INDEX pgroonga_index ON logs USING pgroonga (record);
+SET enable_seqscan = off;
+SET enable_indexscan = off;
+SET enable_bitmapscan = on;
+SELECT id, record
+  FROM logs
+ WHERE record @@ 'paths @ ".message" && type == "object"'
+ ORDER BY id;
+ id |                     record                     
+----+------------------------------------------------
+  1 | {"message": {"code": 100, "content": "hello"}}
+(1 row)
+
+DROP TABLE logs;

  Added: expected/jsonb/object/indexscan.out (+21 -0) 100644
===================================================================
--- /dev/null
+++ expected/jsonb/object/indexscan.out    2015-09-27 18:13:47 +0900 (d3d3728)
@@ -0,0 +1,21 @@
+CREATE TABLE logs (
+  id int,
+  record jsonb
+);
+INSERT INTO logs VALUES (1, '{"message": {"code": 100, "content": "hello"}}');
+INSERT INTO logs VALUES (1, '{"message": "hello"}');
+INSERT INTO logs VALUES (1, '{"message": ["hello", "world"]}');
+CREATE INDEX pgroonga_index ON logs USING pgroonga (record);
+SET enable_seqscan = off;
+SET enable_indexscan = on;
+SET enable_bitmapscan = off;
+SELECT id, record
+  FROM logs
+ WHERE record @@ 'paths @ ".message" && type == "object"'
+ ORDER BY id;
+ id |                     record                     
+----+------------------------------------------------
+  1 | {"message": {"code": 100, "content": "hello"}}
+(1 row)
+
+DROP TABLE logs;

  Modified: pgroonga.c (+179 -37)
===================================================================
--- pgroonga.c    2015-09-27 16:57:55 +0900 (529bf5d)
+++ pgroonga.c    2015-09-27 18:13:47 +0900 (12a5037)
@@ -812,6 +812,58 @@ PGrnConvertDatum(Datum datum, Oid typeID, grn_obj *buffer)
 	}
 }
 
+#ifdef JSONBOID
+static void
+PGrnJSONGenerateCompletePath(grn_obj *components, grn_obj *path)
+{
+	unsigned int i, n;
+
+	n = grn_vector_size(ctx, components);
+
+	GRN_TEXT_PUTS(ctx, path, ".");
+	for (i = 0; i < n; i++)
+	{
+		const char *component;
+		unsigned int componentSize;
+		grn_id domain;
+
+		componentSize = grn_vector_get_element(ctx,
+											   components,
+											   i,
+											   &component,
+											   NULL,
+											   &domain);
+		if (domain == GRN_DB_UINT32)
+		{
+			GRN_TEXT_PUTS(ctx, path, "[]");
+		}
+		else
+		{
+			GRN_TEXT_PUTS(ctx, path, "[");
+			grn_text_esc(ctx, path, component, componentSize);
+			GRN_TEXT_PUTS(ctx, path, "]");
+		}
+	}
+}
+
+static const char *
+PGrnJSONIteratorTokenName(JsonbIteratorToken token)
+{
+	static char *names[] = {
+		"WJB_DONE",
+		"WJB_KEY",
+		"WJB_VALUE",
+		"WJB_ELEM",
+		"WJB_BEGIN_ARRAY",
+		"WJB_END_ARRAY",
+		"WJB_BEGIN_OBJECT",
+		"WJB_END_OBJECT"
+	};
+
+	return names[token];
+}
+#endif
+
 static grn_obj *
 PGrnLookup(const char *name, int errorLevel)
 {
@@ -1033,6 +1085,10 @@ PGrnCreateDataColumnsForJSON(PGrnCreateData *data)
 	}
 
 	PGrnCreateColumn(data->jsonValuesTable,
+					 "path",
+					 GRN_OBJ_COLUMN_SCALAR,
+					 data->jsonPathsTable);
+	PGrnCreateColumn(data->jsonValuesTable,
 					 "paths",
 					 GRN_OBJ_COLUMN_VECTOR,
 					 data->jsonPathsTable);
@@ -1133,7 +1189,7 @@ PGrnCreateIndexColumnsForJSON(PGrnCreateData *data)
 					 data->sourcesTable);
 	PGrnCreateColumn(data->jsonPathsTable,
 					 PGrnIndexColumnName,
-					 GRN_OBJ_COLUMN_INDEX,
+					 GRN_OBJ_COLUMN_INDEX | GRN_OBJ_WITH_SECTION,
 					 data->jsonValuesTable);
 
 	/* TODO: 4KiB over string value can't be searched. */
@@ -1377,11 +1433,19 @@ PGrnSetSourcesForJSON(Relation index,
 		grn_obj *source;
 		grn_obj *indexColumn;
 
+		GRN_BULK_REWIND(sourceIDs);
+
+		source = PGrnLookupColumn(jsonValuesTable, "path", ERROR);
+		GRN_RECORD_PUT(ctx, sourceIDs, grn_obj_id(ctx, source));
+		grn_obj_unlink(ctx, source);
+
 		source = PGrnLookupColumn(jsonValuesTable, "paths", ERROR);
+		GRN_RECORD_PUT(ctx, sourceIDs, grn_obj_id(ctx, source));
+		grn_obj_unlink(ctx, source);
+
 		indexColumn = PGrnLookupColumn(jsonPathsTable, PGrnIndexColumnName,
 									   ERROR);
-		PGrnSetSource(indexColumn, source, sourceIDs);
-		grn_obj_unlink(ctx, source);
+		grn_obj_set_info(ctx, indexColumn, GRN_INFO_SOURCE, sourceIDs);
 		grn_obj_unlink(ctx, indexColumn);
 	}
 
@@ -2032,6 +2096,7 @@ typedef struct PGrnInsertJSONData
 {
 	grn_obj *jsonPathsTable;
 	grn_obj *jsonValuesTable;
+	grn_obj *pathColumn;
 	grn_obj *pathsColumn;
 	grn_obj *stringColumn;
 	grn_obj *numberColumn;
@@ -2056,6 +2121,8 @@ PGrnInsertJSONDataInit(PGrnInsertJSONData *data,
 	data->jsonPathsTable  = PGrnLookupJSONPathsTable(index, nthValue, ERROR);
 	data->jsonValuesTable = PGrnLookupJSONValuesTable(index, nthValue, ERROR);
 
+	data->pathColumn =
+		PGrnLookupColumn(data->jsonValuesTable, "path", ERROR);
 	data->pathsColumn =
 		PGrnLookupColumn(data->jsonValuesTable, "paths", ERROR);
 	data->stringColumn =
@@ -2099,6 +2166,7 @@ PGrnInsertJSONDataFin(PGrnInsertJSONData *data)
 	grn_obj_unlink(ctx, data->numberColumn);
 	grn_obj_unlink(ctx, data->stringColumn);
 	grn_obj_unlink(ctx, data->pathsColumn);
+	grn_obj_unlink(ctx, data->pathColumn);
 	grn_obj_unlink(ctx, data->jsonValuesTable);
 	grn_obj_unlink(ctx, data->jsonPathsTable);
 }
@@ -2118,16 +2186,24 @@ PGrnInsertJSONGenerateKey(PGrnInsertJSONData *data,
 	{
 		const char *component;
 		unsigned int componentSize;
+		grn_id domain;
 
 		componentSize = grn_vector_get_element(ctx,
 											   &(data->components),
 											   i,
 											   &component,
 											   NULL,
-											   NULL);
-		GRN_TEXT_PUTS(ctx, &(data->key), "[");
-		grn_text_esc(ctx, &(data->key), component, componentSize);
-		GRN_TEXT_PUTS(ctx, &(data->key), "]");
+											   &domain);
+		if (domain == GRN_DB_UINT32)
+		{
+			GRN_TEXT_PUTS(ctx, &(data->key), "[]");
+		}
+		else
+		{
+			GRN_TEXT_PUTS(ctx, &(data->key), "[");
+			grn_text_esc(ctx, &(data->key), component, componentSize);
+			GRN_TEXT_PUTS(ctx, &(data->key), "]");
+		}
 	}
 
 	GRN_TEXT_PUTS(ctx, &(data->key), "|");
@@ -2174,16 +2250,19 @@ PGrnInsertJSONGenerateSubPathsRecursive(PGrnInsertJSONData *data,
 	{
 		const char *component;
 		unsigned int componentSize;
+		grn_id domain;
 
 		componentSize = grn_vector_get_element(ctx,
 											   &(data->components),
 											   i,
 											   &component,
 											   NULL,
-											   NULL);
-		GRN_TEXT_PUT(ctx, &(data->path), component, componentSize);
-		if (i != current)
+											   &domain);
+		if (domain == GRN_DB_UINT32)
+			continue;
+		if (GRN_TEXT_LEN(&(data->path)) > 0)
 			GRN_TEXT_PUTS(ctx, &(data->path), ".");
+		GRN_TEXT_PUT(ctx, &(data->path), component, componentSize);
 	}
 	PGrnInsertJSONAddPath(data);
 
@@ -2192,13 +2271,16 @@ PGrnInsertJSONGenerateSubPathsRecursive(PGrnInsertJSONData *data,
 	{
 		const char *component;
 		unsigned int componentSize;
+		grn_id domain;
 
 		componentSize = grn_vector_get_element(ctx,
 											   &(data->components),
 											   i,
 											   &component,
 											   NULL,
-											   NULL);
+											   &domain);
+		if (domain == GRN_DB_UINT32)
+			continue;
 		GRN_TEXT_PUTS(ctx, &(data->path), "[");
 		grn_text_esc(ctx, &(data->path), component, componentSize);
 		GRN_TEXT_PUTS(ctx, &(data->path), "]");
@@ -2225,14 +2307,17 @@ PGrnInsertJSONGeneratePathsRecursive(PGrnInsertJSONData *data,
 	{
 		const char *component;
 		unsigned int componentSize;
+		grn_id domain;
 
 		componentSize = grn_vector_get_element(ctx,
 											   &(data->components),
 											   i,
 											   &component,
 											   NULL,
-											   NULL);
-		if (i > 0)
+											   &domain);
+		if (domain == GRN_DB_UINT32)
+			continue;
+		if (GRN_TEXT_LEN(&(data->path)) > 1)
 			GRN_TEXT_PUTS(ctx, &(data->path), ".");
 		GRN_TEXT_PUT(ctx, &(data->path), component, componentSize);
 	}
@@ -2244,13 +2329,16 @@ PGrnInsertJSONGeneratePathsRecursive(PGrnInsertJSONData *data,
 	{
 		const char *component;
 		unsigned int componentSize;
+		grn_id domain;
 
 		componentSize = grn_vector_get_element(ctx,
 											   &(data->components),
 											   i,
 											   &component,
 											   NULL,
-											   NULL);
+											   &domain);
+		if (domain == GRN_DB_UINT32)
+			continue;
 		GRN_TEXT_PUTS(ctx, &(data->path), "[");
 		grn_text_esc(ctx, &(data->path), component, componentSize);
 		GRN_TEXT_PUTS(ctx, &(data->path), "]");
@@ -2288,6 +2376,12 @@ PGrnInsertJSONValueSet(PGrnInsertJSONData *data,
 	if (!added)
 		return;
 
+	GRN_BULK_REWIND(&(data->path));
+	PGrnJSONGenerateCompletePath(&(data->components), &(data->path));
+	if (GRN_TEXT_LEN(&(data->path)) < GRN_TABLE_MAX_KEY_SIZE)
+		grn_obj_set_value(ctx, data->pathColumn, valueID,
+						  &(data->path), GRN_OBJ_SET);
+
 	PGrnInsertJSONGeneratePaths(data);
 	grn_obj_set_value(ctx, data->pathsColumn, valueID,
 					  &(data->pathIDs), GRN_OBJ_SET);
@@ -2366,21 +2460,34 @@ PGrnInsertJSON(JsonbIterator **iter, PGrnInsertJSONData *data)
 								   GRN_DB_SHORT_TEXT);
 			break;
 		case WJB_VALUE:
-		{
-			const char *component;
 			PGrnInsertJSONValue(iter, &value, data);
-			grn_vector_pop_element(ctx, &(data->components), &component,
-								   NULL, NULL);
+			{
+				const char *component;
+				grn_vector_pop_element(ctx, &(data->components), &component,
+									   NULL, NULL);
+			}
 			break;
-		}
 		case WJB_ELEM:
 			PGrnInsertJSONValue(iter, &value, data);
 			break;
 		case WJB_BEGIN_ARRAY:
+		{
+			uint32_t nElements = value.val.array.nElems;
+			grn_vector_add_element(ctx, &(data->components),
+								   (const char *)&nElements,
+								   sizeof(uint32_t),
+								   0,
+								   GRN_DB_UINT32);
 			PGrnInsertJSONValueSet(data, NULL, "array");
 			break;
+		}
 		case WJB_END_ARRAY:
+		{
+			const char *component;
+			grn_vector_pop_element(ctx, &(data->components), &component,
+								   NULL, NULL);
 			break;
+		}
 		case WJB_BEGIN_OBJECT:
 			PGrnInsertJSONValueSet(data, NULL, "object");
 			break;
@@ -2743,6 +2850,30 @@ PGrnSearchBuildConditionJSONQuery(PGrnSearchData *data,
 }
 
 static void
+PGrnSearchBuildConditionJSONContainType(PGrnSearchData *data,
+										grn_obj *subFilter,
+										grn_obj *targetColumn,
+										grn_obj *components,
+										const char *typeName,
+										unsigned int *nthCondition)
+{
+	GRN_BULK_REWIND(&buffer);
+
+	GRN_TEXT_PUTS(ctx, &buffer, "type == ");
+	grn_text_esc(ctx, &buffer, typeName, strlen(typeName));
+
+	GRN_BULK_REWIND(&pathBuffer);
+	PGrnJSONGenerateCompletePath(components, &pathBuffer);
+	GRN_TEXT_PUTS(ctx, &buffer, " && path == ");
+	grn_text_esc(ctx, &buffer,
+				 GRN_TEXT_VALUE(&pathBuffer),
+				 GRN_TEXT_LEN(&pathBuffer));
+
+	PGrnSearchBuildConditionJSONQuery(data, subFilter, targetColumn,
+									  &buffer, nthCondition);
+}
+
+static void
 PGrnSearchBuildConditionJSONContainValue(PGrnSearchData *data,
 										 grn_obj *subFilter,
 										 grn_obj *targetColumn,
@@ -2796,24 +2927,8 @@ PGrnSearchBuildConditionJSONContainValue(PGrnSearchData *data,
 	}
 
 	GRN_BULK_REWIND(&pathBuffer);
-	GRN_TEXT_PUTS(ctx, &pathBuffer, ".");
-	n = grn_vector_size(ctx, components);
-	for (i = 0; i < n; i++)
-	{
-		const char *component;
-		unsigned int componentSize;
-
-		componentSize = grn_vector_get_element(ctx,
-											   components,
-											   i,
-											   &component,
-											   NULL,
-											   NULL);
-		GRN_TEXT_PUTS(ctx, &pathBuffer, "[");
-		grn_text_esc(ctx, &pathBuffer, component, componentSize);
-		GRN_TEXT_PUTS(ctx, &pathBuffer, "]");
-	}
-	GRN_TEXT_PUTS(ctx, &buffer, " && paths @ ");
+	PGrnJSONGenerateCompletePath(components, &pathBuffer);
+	GRN_TEXT_PUTS(ctx, &buffer, " && path == ");
 	grn_text_esc(ctx, &buffer,
 				 GRN_TEXT_VALUE(&pathBuffer),
 				 GRN_TEXT_LEN(&pathBuffer));
@@ -2867,10 +2982,37 @@ PGrnSearchBuildConditionJSONContain(PGrnSearchData *data,
 													 &nthCondition);
 			break;
 		case WJB_BEGIN_ARRAY:
+		{
+			uint32_t nElements = value.val.array.nElems;
+			grn_vector_add_element(ctx, &components,
+								   (const char *)&nElements,
+								   sizeof(uint32_t),
+								   0,
+								   GRN_DB_UINT32);
+			if (nElements == 0)
+				PGrnSearchBuildConditionJSONContainType(data,
+														subFilter,
+														targetColumn,
+														&components,
+														"array",
+														&nthCondition);
 			break;
+		}
 		case WJB_END_ARRAY:
+		{
+			const char *component;
+			grn_vector_pop_element(ctx, &components, &component,
+								   NULL, NULL);
 			break;
+		}
 		case WJB_BEGIN_OBJECT:
+			if (value.val.object.nPairs == 0)
+				PGrnSearchBuildConditionJSONContainType(data,
+														subFilter,
+														targetColumn,
+														&components,
+														"object",
+														&nthCondition);
 			break;
 		case WJB_END_OBJECT:
 			break;

  Added: sql/jsonb/contain/array/empty.sql (+24 -0) 100644
===================================================================
--- /dev/null
+++ sql/jsonb/contain/array/empty.sql    2015-09-27 18:13:47 +0900 (000350b)
@@ -0,0 +1,24 @@
+CREATE TABLE logs (
+  id int,
+  record jsonb
+);
+
+INSERT INTO logs VALUES (1, '[]');
+INSERT INTO logs VALUES (2, '[100]');
+INSERT INTO logs VALUES (3, '["hello"]');
+INSERT INTO logs VALUES (4, '[true]');
+INSERT INTO logs VALUES (5, '[{"object": "value"}]');
+INSERT INTO logs VALUES (6, '{"object": []}');
+
+CREATE INDEX pgroonga_index ON logs USING pgroonga (record);
+
+SET enable_seqscan = off;
+SET enable_indexscan = on;
+SET enable_bitmapscan = off;
+
+SELECT id, record
+  FROM logs
+ WHERE record @> '[]'::jsonb
+ ORDER BY id;
+
+DROP TABLE logs;

  Renamed: sql/jsonb/contain/array/multiple-elements.sql (+0 -0) 100%
===================================================================

  Added: sql/jsonb/contain/object/empty.sql (+24 -0) 100644
===================================================================
--- /dev/null
+++ sql/jsonb/contain/object/empty.sql    2015-09-27 18:13:47 +0900 (b04df59)
@@ -0,0 +1,24 @@
+CREATE TABLE logs (
+  id int,
+  record jsonb
+);
+
+INSERT INTO logs VALUES (1, '{}');
+INSERT INTO logs VALUES (2, '{"key": 100}');
+INSERT INTO logs VALUES (3, '{"key": "hello"}');
+INSERT INTO logs VALUES (4, '{"key": true}');
+INSERT INTO logs VALUES (5, '{"key": []}');
+INSERT INTO logs VALUES (6, '[{}]');
+
+CREATE INDEX pgroonga_index ON logs USING pgroonga (record);
+
+SET enable_seqscan = off;
+SET enable_indexscan = on;
+SET enable_bitmapscan = off;
+
+SELECT id, record
+  FROM logs
+ WHERE record @> '{}'::jsonb
+ ORDER BY id;
+
+DROP TABLE logs;
-------------- next part --------------
HTML����������������������������...
下載 



More information about the Groonga-commit mailing list
Back to archive index