Loopback 4: How to query an array of objects - mysql

I have been unable to query objects based on a property in an array of objects.
I am trying to query all orders that have the event with id 7:
const orders = await this.orderRepository.find({where: {events: {elemMatch: {'id': event.id}}}});
The above gives me the following error:
ER_PARSE_ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''{\"id\":\"7\"}
If i try the following filter, I always get an empty array back:
{where: {events: {like: '%id%'}}}
What is the correct approach for Loopback 4?
UPDATE:
I am using MySQL 8.0.
This is the definition of events in my order model:
#property({
type: 'array',
itemType: 'object',
required: false,
})
events: CartItem[] | null;

Solution
Since you are using the MySQL loopback connector to connect to your MySQL database, currently this connector treats both String/JSON as VARCHAR. As such, you could try the following modification to like
{where: {events: {like: '%id:'+7+'%'}}}
or
const orders = await this.orderRepository.find({
where: {
events: {
like: '%id:'+event.id+'%'
}
}
});
or using regular expressions
const orders = await this.orderRepository.find({
where: {
events: {
regexp: '.*id:'+event.id+'.*'
}
}
});
const orders = await this.orderRepository.find({
where: {
events: {
regexp: new RegExp(".*id:"+event.id+".*")
}
}
});
in an attempt to match the json pattern {id:7,name:'Event 7'} where in this case the value inside id could be 7.
Assumptions
Based on your question and the mysql error shown, the following assumptions were made:
Schema (MySQL v5.7)
create table samples(id int primary key auto_increment, events varchar(400));
insert into samples(events) values
('[{id:3,name:\"Boscobel\"},{id:4,name:\"Rays\"}]'),
('[{id:7,name:\"Boscobel 7\"},{id:8,name:\"Rays 8\"}]');
Should Receive Results
Query #1
select * from samples where events like '%id\:7%';
| id | events |
| --- | ----------------------------------------------- |
| 2 | [{id:7,name:"Boscobel 7"},{id:8,name:"Rays 8"}] |
Query #2
select * from samples where events like '%id:7%';
| id | events |
| --- | ----------------------------------------------- |
| 2 | [{id:7,name:"Boscobel 7"},{id:8,name:"Rays 8"}] |
Should Not Receive Results
Query #3
select * from samples where events like '%id\:70%';
There are no results to be displayed.
Query #4
select * from samples where events like '%id:200%';
There are no results to be displayed.
View on DB Fiddle

Related

SQL query for adding up columns

| ORDERID | subtotal |
--------------------
| 1000 | 50 |
| 1000 | 100 |
| 1001 | 75 |
Need a query that sum up all the prices when the orderid is 1000 and return 150.
I've tried sum function with where clause but its not working.
Here is my code:
public double findTotalSubtotal(String order_id) {
double sum = 0 ;
PreparedStatement ps;
try {
ps = connect.prepareStatement("select sum(subtotal) from order where ORDERID = ?");
ps.setString(1,order_id);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
int c = rs.getInt(1);
sum = sum + c;
}
}catch(Exception ex) {
ex.printStackTrace();
}
return sum;
}
Any my error:
You have an error in your SQL syntax; check the manual that corresponds to
your MariaDB server version for the right syntax to use near 'order where
ORDERID = '1000'' at line 1
Edited to include actual table name.
With the additional info of what your table name is, I can see what the problem is. "order" is a SQL keyword, so you can't use it as-is as a table name in a query. You have to surround it with square brackets backtics (because MariaDB):
select sum(price) from `order` where ORDERID = 1000
This assumes that the price is stored as a numerical type. Normally that's a pretty safe assumption, but if you're storing it as a VARCHAR, this won't work (and you should probably change the table structure to use a more appropriate type).

Mysql - update + insert json

I got a table that has a JSON field. The default value for the field is "NULL" - now I'd like to update a single field of the JSON data.
| ----------------- |
| [int] | [JSON] |
| xy | ipdata |
| ----------------- |
So the field could be something like this:
{ ip: "233.233.233.233", "data": "test", "name": "Peterson", "full_name": "Hanson Peterson" }
So I'd like to update the IP.
update table set ipdata = JSON_SET(ipdata, "$.ip", "newIp") where xy = 2;
But what happens if the field is NULL? The query above does not seems to "create" a new JSON with just the field IP. It just does nothing.
How can I tell mySql to insert the {"ip": "newIp"} if the field is empty and otherwise just update the ip json key?
You can use Case .. When to handle Null. When the field is null, you can instead create Json_object() and set it:
UPDATE table
SET ipdata = CASE WHEN ipdata IS NULL THEN JSON_OBJECT("ip", "newIp")
ELSE JSON_SET(ipdata, "$.ip", "newIp")
END
WHERE xy = "xy";

Query products with Doctrine ind Akeneo

I like to fetch some products from the Database with a custom command in akeneo.
I'm using the ProductRepositoryInterface
public function read()
{
return $this->repository->findBy(
[
'enabled' => true,
'family' => ['projector', 'projector_child', 'projector_parent'],
]
);
}
And this is the generated query:
SELECT t0.id AS id1, t0.is_enabled AS is_enabled2, t0.created AS created3, t0.updated AS updated4, t0.family_id AS family_id5 FROM pim_catalog_product t0 WHERE t0.is_enabled = ? AND t0.family_id IN (?)
As you can see in the Statement, the family is threaded as an Id. But I want to search by the family code.
What I have to change?
In the Pim/Component/Catalog/Model/AbstractProduct is an attribute for the family and familyId. So there have to be a way to query for the family code.
Maybe it's relevant, but this is an Akeneo 1.6 installation.
So first, to query products in Akeneo, you should use the Product Query Builder (PQB). If you're using the 1.6, here is the link to the documentation to use it, it's pretty straightforward: https://docs.akeneo.com/1.6/cookbook/catalog/product/query.html
To have an exhaustive list of the filters on attributes & fields that can be used with the PQB, you can use the php app/console pim:product:query-help command on your PIM.
As you noticed, the family is not an attribute but a field, you'll find it in the field filters of the command above:
php app/console pim:product:query-help
Useable field filters...
+-----------------+--------------------------------+-----------------------------------------------------------+
| field | operators | filter_class |
+-----------------+--------------------------------+-----------------------------------------------------------+
| family | IN, NOT IN, EMPTY, NOT EMPTY | Pim\Bundle\CatalogBundle\Doctrine\ORM\Filter\FamilyFilter |
| family.id | IN, NOT IN, EMPTY, NOT EMPTY | Pim\Bundle\CatalogBundle\Doctrine\ORM\Filter\FamilyFilter |
| family.code | IN, NOT IN, EMPTY, NOT EMPTY | Pim\Bundle\CatalogBundle\Doctrine\ORM\Filter\FamilyFilter |
+-----------------+--------------------------------+-----------------------------------------------------------+
You can see now that you can search on the family.code field.
For your example, you'll end up with something like this:
<?php
// Get a new instance of the PQB
$pqbFactory = $this->getContainer()->get('pim_catalog.query.product_query_builder_factory');
$pqb = $pqbFactory->create([
'default_locale' => 'en_US',
'default_scope' => 'ecommerce'
]);
// Now you can search for products with your family codes
$pqb->addFilter(
'family.code',
'IN',
['projector', 'projector_child', 'projector_parent']
);
// Retrieve your products
$productsCursor = $pqb->execute();
foreach ($productsCursor as $product) {
// your custom logic
}

How to find if a function exists in PostgreSQL?

Unlike tables or sequences, user-defined functions cannot be found through pg_class. There are questions on how find a list of all functions to delete or grant them, but how to find an individual function (with known name and argument types) is not self-evident from them. So how to find whether a function exists or not?
EDIT: I want to use it in a function, in automated manner. Which solution is the best performance-wise? Trapping errors is quite expensive, so I guess the best solution for me would be something without the extra step of translating error to false, but I might be wrong in this assumption.
Yes, you cannot to find functions in pg_class because functions are stored on system table pg_proc
postgres-# \df
List of functions
Schema | Name | Result data type | Argument data types | Type
--------+--------------------+------------------+----------------------+--------
public | foo | integer | a integer, b integer | normal
public | function_arguments | text | oid | normal
(2 rows)
Query for list of custom functions based on pg_proc is simply
postgres=# select p.oid::regprocedure
from pg_proc p
join pg_namespace n
on p.pronamespace = n.oid
where n.nspname not in ('pg_catalog', 'information_schema');
oid
-------------------------
foo(integer,integer)
function_arguments(oid)
(2 rows)
Most simply and fastest tests on functions existence are casting (without parameters) to regproc or regprocedure (with parameters):
postgres=# select 'foo'::regproc;
regproc
---------
foo
(1 row)
postgres=# select 'foox'::regproc;
ERROR: function "foox" does not exist
LINE 1: select 'foox'::regproc;
^
postgres=# select 'foo(int, int)'::regprocedure;
regprocedure
----------------------
foo(integer,integer)
(1 row)
postgres=# select 'foo(int, text)'::regprocedure;
ERROR: function "foo(int, text)" does not exist
LINE 1: select 'foo(int, text)'::regprocedure;
^
or you can do some similar with test against pg_proc
postgres=# select exists(select * from pg_proc where proname = 'foo');
exists
--------
t
(1 row)
postgres=# select exists(select *
from pg_proc
where proname = 'foo'
and function_arguments(oid) = 'integer, integer');
exists
--------
t
(1 row)
where:
CREATE OR REPLACE FUNCTION public.function_arguments(oid)
RETURNS text LANGUAGE sql AS $function$
select string_agg(par, ', ')
from (select format_type(unnest(proargtypes), null) par
from pg_proc where oid = $1) x
$function$
or you can use buildin functions:pg_get_function_arguments
p.s. trick for simply orientation in system catalog. Use a psql option -E:
[pavel#localhost ~]$ psql -E postgres
psql (9.2.8, server 9.5devel)
Type "help" for help.
postgres=# \df
********* QUERY **********
SELECT n.nspname as "Schema",
p.proname as "Name",
pg_catalog.pg_get_function_result(p.oid) as "Result data type",
pg_catalog.pg_get_function_arguments(p.oid) as "Argument data types",
CASE
WHEN p.proisagg THEN 'agg'
WHEN p.proiswindow THEN 'window'
WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN 'trigger'
ELSE 'normal'
END as "Type"
FROM pg_catalog.pg_proc p
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
WHERE pg_catalog.pg_function_is_visible(p.oid)
AND n.nspname <> 'pg_catalog'
AND n.nspname <> 'information_schema'
ORDER BY 1, 2, 4;
**************************
List of functions
Schema | Name | Result data type | Argument data types | Type
--------+--------------------+------------------+----------------------+--------
public | foo | integer | a integer, b integer | normal
public | function_arguments | text | oid | normal
(2 rows)
I think the easiest way would be to use pg_get_functiondef().
If it returns something, the function is there, otherwise the function does not exist:
select pg_get_functiondef('some_function()'::regprocedure);
select pg_get_functiondef('some_function(integer)'::regprocedure);
The drawback is that it will produce an error if the function isn't there instead of simply returning an empty result. But this could e.g. be overcome by writing a PL/pgSQL function that catches the exception and returns false instead.
Based on #PavelStehule answer this is how I am checking this in my scripts (using postgres exceptions and available exception codes)
DO $_$
BEGIN
BEGIN
SELECT 'some_schema.some_function(text)'::regprocedure;
EXCEPTION WHEN undefined_function THEN
-- do something here, i.e. create function
END;
END $_$;
Late to the party,
but it could be something like this (don't use select instead of perform if you are not using the result or you would get an error complaining about it :
ERROR: query has no destination for result data
So the following code will work :
DO $$
BEGIN
BEGIN
perform pg_get_functiondef('some_function()'::regprocedure);
raise notice 'it exists!';
EXCEPTION WHEN undefined_function THEN
raise notice 'Does not exist';
END;
END $$;

Rails Console Differing MySQL Results

I am having trouble using a LEFT OUTER JOIN with Rails. I think I have my Ruby code down but when I enter it into the rails console I get one result and when I copy the produced MySQL I get another result. An entire column (value) is missing in the rails console. Why is it that the SQL is the exact same but I am getting different results? Ideally, I want the results to include the value column. As always any help would be greatly appreciated.
Rails Console Response
2.0.0-p353 :023 > SecurityItem.joins("LEFT OUTER JOIN security_role_permissions ON security_role_permissions.security_item_id = security_items.id AND security_role_permissions.role_id ='1'").select("security_items.name, security_role_permissions.value")
SecurityItem Load (0.7ms) SELECT security_items.name, security_role_permissions.value FROM `security_items` LEFT OUTER JOIN security_role_permissions ON security_role_permissions.security_item_id = security_items.id AND security_role_permissions.role_id ='1
=> #<ActiveRecord::Relation
[#<SecurityItem id: nil, key: "ChangePassword", name: "Change Password">,
#<SecurityItem id: nil, key: "AddUsers", name: "Add Users">,
#<SecurityItem id: nil, key: "UpdateUsers", name: "Update Users">,
#<SecurityItem id: nil, key: "DeleteUsers", name: "Delete Users">]>
MySQL Response
mysql> SELECT security_items.name, security_role_permissions.value FROM `security_items` LEFT OUTER JOIN security_role_permissions ON security_role_permissions.security_item_id = security_items.id AND security_role_permissions.role_id ='1';
+-----------------+-------+
| name | value |
+-----------------+-------+
| Change Password | 1 |
| Add Users | NULL |
| Update Users | NULL |
| Delete Users | NULL |
+-----------------+-------+
Only the attributes from the SecurityItem class appear in the return result. However, you can access the attributes from the joined table through the 'attributes' method.
security_items = SecurityItem.joins("LEFT OUTER JOIN security_role_permissions
ON security_role_permissions.key = security_items.key AND
security_role_permissions.role_id ='1'").select("security_items.name,
security_items.key, security_role_permissions.permission")
security_items.each { |security_item| puts security_item.attributes['permission'] }