Jed Rembold
April 2, 2025
postgrest/postgrestPGRST_DB_URI is the same address we’ve
used several times already to connect to a database:
postgres://user:password@host:port/database--net=host
flag when running the containerhost.docker.internal
instead of localhost in the URIapiA fundamental part of database management is user management, which are called roles in Postgres
You can create new roles with various permission attributes
CREATE ROLE |||name||| |||attributes|||;You can see all existing roles in the build-in table!
SELECT * FROM pg_roles;A little confusingly, roles can represent either individuals or groups
For anonymous validation, we can create a role that is not allowed to login
CREATE ROLE web_anon NOLOGIN;Note that this will NOT change the
PGRST_DB_URI login. It isn’t a bad idea to
login though with a non-superuser account. So,
optionally:
CREATE ROLE web_auth
NOINHERIT LOGIN PASSWORD 'secretsecret';
GRANT web_anon TO web_auth;
then use that for your URI:
postgresql://web_auth:secretsecret@localhost:port/databaseRoles by themselves have very limited permissions! Need to explicitly grant them (unless they inherited them elsewhere)
We need to GRANT
USAGE rights to schemas
GRANT USAGE ON SCHEMA api TO web_anon;We need to GRANT
SELECT rights to tables/views
GRANT SELECT ON api.|||table name||| TO web_anon;PGRST_DB_SCHEMA: The schema that we want
PostgREST to generate endpoints for (and the one we just gave
permissions toPGRST_DB_ANON_ROLE: The role we created
and gave the permissions to (web_anon)PGRST_OPENAPI_SERVER_PROXY_URI: Set to
localhost or to your PostgREST server URLswaggerapi/swagger-ui on DockerHubAPI_URL which should match the proxy URI we
just defined for PostgREST
| Function | Description |
|---|---|
|||str₁||| || |||str₂||| |
Concatenates string 1 and string 2 together |
upper(|||str|||) |
Converts a string to all uppercase characters |
lower(|||str|||) |
Converts a string to all lowercase characters |
char_length(|||str|||) |
Returns the number of characters in the string |
position(|||str||| IN |||substr|||) |
Find the number of the character where the substring begins |
trim(|||opt||| |||chr||| FROM |||str|||) |
Removes the given characters from the string, optionally taking from the leading or trailing edge |
substring(|||str||| FROM |||n||| FOR |||l|||) |
Returns the portion of the string starting at position n and continuing for l characters |
| Function | Description |
|---|---|
initcap(|||str|||) |
Converts the first character of each word to uppercase, and the rest lower |
left(|||str|||,|||n|||) |
Returns the first n characters of the string |
right(|||str|||,|||n|||) |
Returns the last n characters of the string |
ltrim(|||str|||,|||chr|||) |
Remove the characters (space by default) from the start of the string |
rtrim(|||str|||,|||chr|||) |
Remove the characters (space by default) from the end of the string |
replace(|||str|||,|||from|||,|||to|||) |
Replaces all occurrences of from in the string to |
length(|||str|||) |
Returns the number of characters in the string |
substr(|||str|||, |||n|||, |||l|||) |
Returns the portion of the string starting at position n and continuing l characters |
LIKE and LIKE
% and _| Expression | Description |
|---|---|
. |
Matches any character except a new line |
[abc] |
Matches any character in the square brackets (a or b or c) |
[a-z] |
Matches a range of characters (all lowercase letters here) |
[^a-z] |
Caret negates what follows (so no lowercase letters here) |
\w |
Any word character, digit or underscore |
\d |
Any digit |
\s |
A space |
\t |
A tab character |
\n |
A newline character |
| Expression | Description |
|---|---|
^ |
Match at the start of the string |
$ |
Match at the end of the string |
? |
Get the preceding match 0 or one time |
* |
Get the preceding match zero or more times |
+ |
Get the preceding match one or more times |
{m} |
Get the preceding match exactly m times |
{m,n} |
Get the preceding match between m and n times |
a|b |
Match on either a or b |
( ) |
Create a capture group or set precedence |
(?: ) |
Negate reporting a capture group |
\({ } [ ] / \ + * . $ ^ | ?/i means that matches will be case
insensitive/g means that all instances of the match
will be returned, not just the first/m allows the anchor characters
(^ and $) to
operate on each line, not just across the entire string.~ is a case sensitive match using the
following regex~* is a case insensitive match using the
following regex! in front to negate
the search (where things do not match the regex)SELECT colname
FROM tablename
WHERE colname ~ '[a-z]*\s\d{2}';
Another hugely common use of regex is to extract only the data you want from a much larger string
This can be particularly useful when cleaning data or constructing useful database tables
Since PostgreSQL 15, you have had a perfect tool to accomplish
this easily: regexp_substr
SELECT
regexp_substr('today is March 21, 2024', '\d{4}');regexp_match(|||str|||, |||regex|||)
returns the first matching instance in the string
[N] at
the end after wrapping entire expression in ()UNNESTSELECT (regexp_match('today is March 15, 2022', '\d{4}'))[1];
regexp_replace(|||text|||, |||re|||, |||replacement|||)
will replace the first matches of re in text with the replacement
'g' argument on the end to do
so globally (replacing all matches)regexp_split_to_array(|||text|||, |||re|||)
will split the text into an array on each match to reregexp_split_to_table(|||text|||, |||re|||)
will split the text into a table column on each match to re. This is
just like unnesting the array.1 1/4 form)ts_vectorts_queryto_tsvector will break a
string down into its component lexemes, and keep track of where each
occured in the original stringSELECT to_tsvector('I flew back to Salem on Saturday');
>> 'back':3 'flew':2 'saturday':7 'salem':5
to_tsvectorto_tsqueryto_tsquery takes a sequence of words with
symbols connecting them conveying relationships
& - and| - or! - not<-> - followed bySELECT to_tsquery('fly & monday')To actually complete a full text search, you ask Postgres to look
through a tsvector object for a particular
tsquery
The syntax to do so utilizes the match operator, which is two
“at” symbols: @@
SELECT |||some_tsvector||| @@ |||some_tsquery|||;Using @@ is a true/false search, so
the query is either found or not
WHERE as well!Individual tsvectors can not be easily
ordered, so indexing a column with tsvector
contents using the normal B-Tree method would not be effective
For this sort of content, use a Generalized Inverted Index or GIN indexing method instead
CREATE INDEX |||idx_name||| ON |||tab_name|||
USING GIN (|||col_name|||);Seriously consider adding your
tsvector column as an index, as it can
significantly speed up these sorts of searches
ts_headline function can capture
snippets of text around a match and display them
ts_headline operates on the original
text, not the tsvector! This will absolutely make it
slower, so use it wisely!ts_headline:
StartSel/StopSel:
the delimiters that will showcase the wordMinWords/MaxWords:
the min or max number of words to show around the matchMaxFragments: a max number of fragments
to show if the match occurs multiple timests_rank will give an arbitrary rank based
on how many times your query words appear in the text
ts_rank_cd does similar, but also
considers the proximity of searched lexemesThe file here contains the
SQL commands to generate and populate a simple table
alice which hold the raw chapter contents of
the book: Alice in Wonderland. You will need to set up your own
tsvector column and index. With yet one more
new partner, see if you can use the data to answer the following: