Many to many relationship when order metter - sql

I have many to many relation between User and Group: group may contains few users and consists in multiply groups.
Server send me json like this, and I need to store it to DB:
group: {
name: "test", id: "UUID"
users: [
{name: "user1", id: "UUID"},
{name: "user2", id: "UUID"},
{name: "user3", id: "UUID"}
]
}
The problem is: users order in group matters - users should be displayed in the same order like in servers json.
The fist solution that I use is add to group field like
user_ids: [user1_uuid, user2_uuid, user3_uuid] and store it into DB in json fromat, but I see 2 problems in this solution:
It contradicts to first normal form (1NF)
When I SELECT values they have wrong order:
SELECT * FROM user WHERE id IN (:user_ids)
How to store it into DB and SELECT?

In SQL, table rows indeed are unordered; the only guaranteed way to get ordered rows is to use ORDER BY.
If the order of your array elements is important, add a column for the array index.

You could use the following schema:
CREATE TABLE user(id integer primary key, name text);
CREATE TABLE grp(id integer primary key, name text);
CREATE TABLE usergrp(userid integer, grpid integer, position integer);
CREATE INDEX positionindex on usergrp(grpid, position);
Then you can retrieve users belonging to a group with the query
SELECT userid FROM usergrp WHERE grpid=? ORDER BY position;
Also, maybe you would like to use the "without rowid" sqlite table storage option for usergrp.

Related

How to insert multiple objects into one table?

There's a table entityLinks and I would like to insert multiple values by one query.
The table was defined using this query:
CREATE TABLE entityLinks(
id INTEGER NOT NULL references posts(id) ON DELETE CASCADE,
tag VARCHAR(255) NOT NULL references tags(tag) ON DELETE CASCADE);
Data intended to be used for insertion looks like this:
Array of tags like ['mytag1', 'mytag2, ...];
id of an entity related to the tags (post for instance).
I can combine them into array of objects ( [{id: 1, tag: 'mytag1'}, {id:1, tag: 'mytag2'}, ... ] ) if needed. Id is the same for all the tags in this query, but is different from one query to another.
I know how to insert multiple tags
INSERT INTO tags(tag)
SELECT * FROM unnest($1::text[])
($1 - is a variable, passed as value like this this.pool.query(query, [tags]) );
... but when I tried the same, the unnest has unnested all levels of arraying ([ [1, 'mytag1'], [1, 'mytag2'],... ] => "1, 'mytag1', 1, 'mytag2', ... ".
And the error was : error: malformed record literal: "1" (1 - is the id)
I tried using an array of objects, but got this error: malformed record literal: "{"id":179,"tag":"myTag1"}"
Basically I would like to insert different tags linked with the same id (the same for one query), but also would be interested to understand how to insert multiple objects at once (probably will be useful in the future).
Thanks in advance!
with help of #Gordon Linoff I've composed the right query
INSERT INTO entityLinks(post_id, tag)
SELECT $1, tag
FROM unnest($2::text[]) as tag;
maybe will be useful for someone in the future.
the data is passed like this:
this.pool.query(queries.addLinks, [post_id, tags]);
post_id: number, tags: string[];
Is this what you want?
INSERT INTO entitlyLinks (id, tag)
SELECT 1, tag
FROM unnest($1::text[]) t(tag);
Note: I would recommend calling id post_id, so it is clear what it refers to.

Is it possible to use primary key along with secondary key in Aerospike for fetching a record?

Is it possible to use primary key along with secondary key. I want to get all records on the basis of primary key and then apply secondary index over it? Is it the correct way to do it or some other approach should be used?
I have a bin with primary key. Once i fetch the record using primary key, i want to get only the subset of the record.
Below is the record example. Also is there a better way to organise the record? Like keeping manager, supervisor and otherStaff as the key and then querying.
{
"name" : "test",
"companyName" : "company1",
"details" : [{
"salary" : 1000,
"designation" : "manager"
},
{
"salary" : 2000,
"designation" : "supervisor"
},
{
"staff" : 500,
"designation" : "otherStaff"
}]
}
It is definitely beneficial if you can query by primary key first. It will be very efficient as the request will land on a single node (as opposed to all nodes in case of secondary index query). And on that node, the primary key index lookup is going to be fast.
When you say secondary index, looks like your requirement is to do additional filteration on the data. If my understanding is correct, you can use the Map API to fetch the keys (or values) that you are explicitly looking for. For this you may need to tweak your data model accordingly. In your example, if you are going to fetch different salaries, may be you should create a map of salaries (instead of "salary" being a key in map). Something like this:
{
"name" : "test",
"companyName" : "company1",
"salaries" : {
"manager" : 1000,
"supervisor" : 2000,
"otherStaff" : 500
}
}
You can then use the getByKeyList() map API to fetch multiple values by specifying the keys (in the map) that you are interested in (say 'manager' and 'otherStaff').
Use a Secondary Index query on the bin containing the primary search data and then use Predicate Filters to further select the records returned.
Example construct - record has country and city bins in namespace test, set testset, secondary index = equality string on country.
// Java statement
Record record = null;
Statement stmt = new Statement();
stmt.setSetName("testset");
stmt.setNamespace("test");
stmt.setIndexName("country_idx");
stmt.setFilter(Filter.equal("country","USA"));
//Additional Predicate Filtering
stmt.setPredExp(
PredExp.stringBin("city"),
PredExp.stringValue(".*Diego"),
//prefix-suffix match: prefix.*suffix, ignore case or newline
PredExp.stringRegex(RegexFlag.ICASE | RegexFlag.NEWLINE)
);

How to model events with locations that have a label and lat, lng coordinates?

Assume a special event is happening at this location:
{
label: 'Mountain View',
lat: 37.4224764,
lng: -122.0842499
}
How would you store this information and why?
1) Single table
events: id place_label coordinates(point)
2) Two separate tables
events: id place_id
places: place_id label coordinates(point)
3) Single table using jsonb data type
events: id place(jsonb)
or another way?
The end goal would be to use geospatial extensions (e.g. EarthDistance, PostGIS) to do queries like "find all events within ___ miles".
Do two separate tables otherwise you suffer the disadvantages of querying a larger table for point-in-polygon than needed.
CREATE TABLE places
(
id_place serial PRIMARY KEY,
name text,
geom geography(POINT)
);
CREATE INDEX ON places USING gist(geom);
CREATE TABLE events
(
id_event serial PRIMARY KEY,
id_place int REFERENCES place,
start_ts timestamp
);
The end goal would be to use geospatial extensions (e.g. EarthDistance, PostGIS) to do queries like "find all events within ___ miles".
SELECT *
FROM events AS e
JOIN places AS p USING (id_place)
WHERE ST_DWithin(p.geom, ST_MakePoint(long,lat)::geography, miles*1609);
That's all events within miles of (long,lat). I wouldn't use earthdistance, I would just use PostGIS.

Row Level Security in Postgres on Normalized Tables

The premise
In documentation, Row Level Security seems great. Based on what I've read I can now stop creating views like this:
SELECT data.*
FROM data
JOIN user_data
ON data.id = user_data.data_id
AND user_data.role = CURRENT_ROLE
The great part is, Postgres has a great analysis for that view starting with an index scan then a hash join on the user_data table, exactly what we want to happen because it's crazy fast. Compare that with a my RLS implementation:
CREATE POLICY data_owner
ON data
FOR ALL
TO user
USING (
(
SELECT TRUE AS BOOL FROM (
SELECT data_id FROM user_data WHERE user_role = CURRENT_USER
) AS user_data WHERE user_data.data_id = data.id
) = true
)
WITH CHECK (TRUE);
This bummer of a policy executes the condition for each row in the data table, instead of optimizing by scoping the query to the rows which our CURRENT_USER has access to, like our view does. To be clear, that means select * from data hits every row in the data table.
The question
How do I write a policy with an inner select which doesn't test said select on every row in the target table. Said another way: how do I get RLS to run my policy on the target table before running the actual query on the result?
p.s. I've left this question someone vague and fiddle-less, mostly because sqlfiddle hasn't hit 9.5 yet. Let me know if I need to add more color or some gists to get my question across.
PostgreSQL may be able to generate a better plan if you phrase the policy like this:
...
USING (EXISTS
(SELECT data_id
FROM user_data
WHERE user_data.data_id = data.id
AND role = current_user
)
)
You should have a (PRIMARY KEY?) index ON user_data (role, data_id) to speed up nested loop joins.
But I think that it would be a better design to include the permission information in the data table itself, perhaps using the name[] type:
CREATE TABLE data(
id integer PRIMARY KEY,
val text,
acl name[] NOT NULL
);
INSERT INTO data VALUES (1, 'one', ARRAY[name 'laurenz', name 'advpg']);
INSERT INTO data VALUES (2, 'two', ARRAY[name 'advpg']);
INSERT INTO data VALUES (3, 'three', ARRAY[name 'laurenz']);
Then you can use a policy like this:
CREATE POLICY data_owner ON data FOR ALL TO PUBLIC
USING (acl #> ARRAY[current_user::name])
WITH CHECK (TRUE);
ALTER TABLE data ENABLE ROW LEVEL SECURITY;
ALTER TABLE data FORCE ROW LEVEL SECURITY;
When I SELECT, I get only the rows for which I have permission:
SELECT id, val FROM data;
id | val
----+-------
1 | one
3 | three
(2 rows)
You can define a GIN index to support that condition:
CREATE INDEX ON data USING gin (acl _name_ops);

How to set primary key on a field in fusion table

I have a table named "Testing". fields in this table are Id,Name and Description.
I want to make Id field as a primary key i.e. only unique values should be entered in this field.
Thanks,
Himanshu
When you do CREATE TABLE
In addition to the column names you specify in the CREATE TABLE statement, Google Fusion Tables will create a column named ROWID that contains an automatically-assigned, unique ID number for each row.
I don't think you can do this. It appears to be a much requested feature: https://code.google.com/p/fusion-tables/issues/detail?id=112
It's not available. I use a workaround that performs a COUNT(ROWID) query for the unqiue ID I'm about to insert and then take steps depending on whether or not that unique ID is already in the table.
The basic algo is something like this (using Google Apps Script):
var check = FusionTables.Query.sql("SELECT COUNT(ROWID) FROM <table ID> WHERE 'Trip ID' = '" + rowID + "'");
if (check.rows[0][0] > 0) {
// ID already exists
} else {
// ID is new, insert the record.
}