PostgreSQL, using xml - postgresql

Here is example table used for getting some basic operations with xml column in postgreSQL table.
DROP TABLE IF EXISTS temp1;
CREATE TABLE temp1(myindex serial PRIMARY KEY, description xml);
INSERT INTO temp1(description)
VALUES
('<?xml version="1.0" encoding="utf-8"?>
<setup xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<DATABASE>herdatabase</DATABASE>
<DBSERVER>127.0.0.1</DBSERVER>
<DBUSER>saly</DBUSER>
<DBPORT>5432</DBPORT>
</setup>'),
('<?xml version="1.0" encoding="utf-8"?>
<setup xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<DATABASE>mydatabase</DATABASE>
<DBSERVER>127.0.0.1</DBSERVER>
<DBUSER>john</DBUSER>
<DBPORT>4424</DBPORT>
</setup>');
I decided to use XML instead of hstore and JSON since I'm working in .NET where XML functions and serialization is well supported and I haven't much of such data so speed is not much important.
From here I try some basic queries to get data.
--1) That one work
SELECT xpath('/setup/DBPORT/text()', description) FROM temp1;
--2) That work but give two arrays with single value
-- How to get one array with 2 values like "{5432, 127.0.0.1}"
SELECT xpath('/setup/DBPORT/text()', description), xpath('/setup/DBSERVER/text()', description) FROM temp1;
--3) How to get description when condition is met?
-- Here I get ERROR: could not identify an equality operator for type xml
SELECT description FROM temp1 WHERE xpath('/setup/DBSERVER/text()', description) = '{127.0.0.1}';
--4) How to get all values when condition is met?
SELECT allvalues FROM temp1 WHERE xpath('/setup/DBUSER/text()', description) = 'john';
How to get working those queries which don't work?

2 - Use the XPath "or" operator, |, to select either a DBPORT or DBSERVER:
SELECT xpath('/setup/DBPORT/text()|/setup/DBSERVER/text()', description)
FROM temp1;
3 - The xpath() function returns an XML array which can be cast to a TEXT array for easier matching to other values:
SELECT description
FROM temp1
WHERE xpath('/setup/DBSERVER/text()', description)::TEXT[] = '{127.0.0.1}'::TEXT[];
4 - Similar to the previous, cast the XML array to a Text array to match to a value:
SELECT xpath('/setup/node()/text()', description)
FROM temp1
WHERE xpath('/setup/DBUSER/text()', description)::TEXT[] = '{john}'::TEXT[];

For the second, you have two arrays, so you can use array_cat():
SELECT array_cat(xpath('/setup/DBPORT/text()', description),
xpath('/setup/DBSERVER/text()', description))
FROM temp1;
For the third, you have one array of values (it's possible that your xpath matches multiple /setup/DBSERVER elements, thus the array type). This takes the first element from the array and casts to text so that you can compare to the string
SELECT description
FROM temp1
WHERE (xpath('/setup/DBSERVER/text()', description))[1]::text = '127.0.0.7';
Finally, you can use an xpath to generate an array of your elements, then unnest() them (so you get one row per element), then use another xpath to get at the element content. This gives the element content, but not the element name - I don't know the xpath to get the tag name off the top of my head.
SELECT xpath('/', unnest(xpath('/setup/*', description)))
FROM temp1
WHERE (xpath('/setup/DBUSER/text()', description))[1]::text = 'john';

Related

Get unique values from PostgreSQL array

This seems like it would be straightforward to do but I just can not figure it out. I have a query that returns an ARRAY of strings in one of the columns. I want that array to only contain unique strings. Here is my query:
SELECT
f."_id",
ARRAY[public.getdomain(f."linkUrl"), public.getdomain(f."sourceUrl")] AS file_domains,
public.getuniqdomains(s."originUrls", s."testUrls") AS source_domains
FROM
files f
LEFT JOIN
sources s
ON
s."_id" = f."sourceId"
Here's an example of a row from my return table
_id
file_domains
source_domains
2574873
{cityofmontclair.org,cityofmontclair.org}
{cityofmontclair.org}
I need file_domains to only contain unique values, IE a 'set' instead of a 'list'. Like this:
_id
file_domains
source_domains
2574873
{cityofmontclair.org}
{cityofmontclair.org}
Use a CASE expression:
CASE WHEN public.getdomain(f."linkUrl") = public.getdomain(f."sourceUrl")
THEN ARRAY[public.getdomain(f."linkUrl")]
ELSE ARRAY[public.getdomain(f."linkUrl"), public.getdomain(f."sourceUrl")]
END

Build jsonb array from jsonb field

I have column options with type jsonb , in format {"names": ["name1", "name2"]} which was created with
UPDATE table1 t1 SET options = (SELECT jsonb_build_object('names', names) FROM table2 t2 WHERE t2.id= t1.id)
and where names have type jsonb array.
SELECT jsonb_typeof(names) FROM table2 give array
Now I want to extract value of names as jsonb array. But query
SELECT jsonb_build_array(options->>'names') FROM table
gave me ["[\"name1\", \"name2\"]"], while I expect ["name1", "name2"]
How can I get value in right format?
The ->> operator will return the value of the field (in your case, a JSON array) as a properly escaped text. What you are looking for is the -> operator instead.
However, note that using the jsonb_build_array on that will return an array containing your original array, which is probably not what you want either; simply using options->'names' should get you what you want.
Actually, you don't need to use jsonb_build_array() function.
Use select options -> 'names' from table; This will fix your issue.
jsonb_build_array() is for generating the array from jsonb object. You are following wrong way. That's why you are getting string like this ["[\"name1\", \"name2\"]"].
Try to execute this sample SQL script:
select j->'names'
from (
select '{"names": ["name1", "name2"]}'::JSONB as j
) as a;

How to split array in json using json_query?

I've got a column in a table that's a json. It contains only values without keys like
Now I'm trying to split the data from the json and create new table using every index of each array as new entry like
I've already tried
SELECT JSON_QUERY(abc) as 'Type', Id as 'ValueId' from Table FOR JSON AUTO
Is there any way to handle splitting given that some arrays might be empty and look like
[]
?
A fairly simply approach would be to use outer apply with openjson.
First, create and populate sample table (Please save us this step in your future questions):
DECLARE #T AS TABLE
(
Id int,
Value nvarchar(20)
)
INSERT INTO #T VALUES
(1, '[10]'),
(2, '[20, 200]'),
(3, '[]'),
(4, '')
The query:
SELECT Id, JsonValues.Value
FROM #T As t
OUTER APPLY
OPENJSON( Value ) As JsonValues
WHERE ISJSON(t.Value) = 1
Results:
Id Value
1 10
2 20
2 200
3 NULL
Note the ISJSON condition in the where clause will prevent exceptions in case the Value column contains anything other than a valid json (an empty array [] is still considered valid for this purpose).
If you don't want to return a row where the json array is empty, use cross apply instead of outer apply.
Your own code calling for FOR JSON AUTO tries to create JSON out of tabular data. But what you really needs seems to be the opposite direction: You want to transform JSON to a result set, a derived table. This is done by OPENJSON.
Your JSON seems to be a very minimalistic array.
You can try something along this.
DECLARE #json NVARCHAR(MAX) =N'[1,2,3]';
SELECT * FROM OPENJSON(#json);
The result returns the zero-based ordinal position in key, the actual value in value and a (very limited) type-enum.
Hint: If you want to use this against a table's column you must use APPLY, something along
SELECT *
FROM YourTable t
OUTER APPLY OPENJSON(t.TheJsonColumn);

How to create and store array of objects in postgresql

In postgresql allowed array types or integer and text.But i need to create array of objects.how can i do that.
myarray text[]; //for text ['a','b','c']
myarray integer[]; //for integer[1,2,3]
I need to create the array like below
[{'dsad':1},{'sdsad':34.6},{'sdsad':23}]
I dont want to use JSON type.Using array type i need to store the array of objects.
If you're running Postgres 9.2+, you can use the JSON type.
For example, we could do
create table jsontest (id serial primary key, data json);
insert into jsontest (data) values ('[{"dsad":1},{"sdsad":34.6},{"sdsad":23}]');
And query the data with
select data->1 from jsontest;
{"sdsad":34.6}
You say:
I dont want to use JSON type
but you cannot use an ordinary array, as PostgreSQL arrays must be of homogenous types. You can't have a 2-dimensional array of text and integer.
What you could do if you don't want to use json is to create a composite type:
CREATE TYPE my_pair AS (blah text, blah2 integer);
SELECT ARRAY[ ROW('dasd',2), ROW('sdsad', 34.6), ROW('sdsad', 23) ]::my_pair[]
which will emit:
array
----------------------------------------
{"(dasd,2)","(sdsad,35)","(sdsad,23)"}
(1 row)
If you don't want that, then json is probably your best bet. Or hstore:
SELECT hstore(ARRAY['a','b','c'], ARRAY[1,2,3]::text[])
hstore
------------------------------
"a"=>"1", "b"=>"2", "c"=>"3"
(1 row)
JSON is your preferred answer, but more info as to why.
You can do something like:
SELECT array_agg(v)
FROM mytable v;
However you get something that looks like this:
{"(dsad,1)","(sdsad,34.6)","(""sdsad,var"",23)"}
It is then up to you to know how to decode this (i.e. column order). This is possible to do programmatically but is much easier with JSON.
It's hacky, but what about using an array for each property in the object (and its corresponding scalar type). If you have a data model layer in your get/read you could put the arrays "back together" into an array of objects and in your save method you would break you objects apart into synchronized arrays. This might be complicated by your example of each object not having the same properties; IDK how you'd store undefined for a property unless you're willing for null to be the same semantically.
It's not entirely clear if you mean json:
# select '[{"dsad":1},{"sdsad":34.6},{"sdsad":23}]'::json;
json
------------------------------------------
[{"dsad":1},{"sdsad":34.6},{"sdsad":23}]
(1 row)
Or an array of json:
# select array['{"dsad":1}', '{"sdsad":34.6}', '{"sdsad":23}']::json[];
array
------------------------------------------------------
{"{\"dsad\":1}","{\"sdsad\":34.6}","{\"sdsad\":23}"}
(1 row)
Or perhaps hstore? If the latter, it's only for key-values pairs, but you can likewise use an array of hstore values.
You can do something like:
SELECT JSON_AGG(v) FROM mytable v;
However you get something that looks like this:
["00000000-0000-0000-0000-000000000001","00000000-0000-0000-0000-000000000002", "00000000-0000-0000-0000-000000000003"]
exemple :
SELECT title, (select JSON_AGG(v.video_id) FROM videos v WHERE v.channel_id = c.channel_id) AS videos FROM channel AS c
Use text[] myarray insted of myarray text[].

Complex SphinxQL Query

I'm trying to write a SphinxQL query that would replicate the following MySQL in a Sphinx RT index:
SELECT id FROM table WHERE colA LIKE 'valA' AND (colB = valB OR colC = valC OR ... colX = valX ... OR colY LIKE 'valY' .. OR colZ LIKE 'valZ')
As you can see I'm trying to get all the rows where one string column matches a certain value, AND matches any one of a list of values, which mixes and matches string and integer columns / values)
This is what I've gotten so far in SphinxQL:
SELECT id, (intColA = intValA OR intColB = intValB ...) as intCheck FROM rt_index WHERE MATCH('#requiredMatch = requiredValue');
The problem I'm running into is in matching all of the potential optional string values. The best possible query (if multiple MATCH statements were allowed and they were allowed as expressions) would be something like
SELECT id, (intColA = intValA OR MATCH('#checkColA valA|valB') OR ...) as optionalMatches FROM rt_index WHERE optionalMatches = 1 AND MATCH('#requireCol requiredVal')
I can see a potential way to do this with CRC32 string conversions and MVA attributes but these aren't supported with RT Indexes and I REALLY would prefer not switch from them.
One way would be to simply convert all your columns to normal fields. Then you can put all this logic inside the MATCH(..). Ie not using attributes.
Yes you can only have one MATCH per query.
Otherwise, yes you could use the CRC trick to make string attributes into integer ones, so can use for filtering.
Not sure why you would need MVA, but they are now supported in RT indexes in 2.0.2