How can I search for arrays that contain strings found in my search key? [duplicate] - sql

This question already has an answer here:
Rails (postgres) query with jsonb array
(1 answer)
Closed 5 years ago.
Say I have a Product table with a json array attribute called "name". For example, Product.first.name == ["large", "black", "hoodie"]. I want to search through my database for Products with names that contain words in my search query. So if I type in "large hoodie", Product.first should be returned in the results.
So first I have to turn the search key into an array of strings:
def search
search_array = params[:search].split(" ")
results = #???
but how can I search for Products with names that include values also contained in search_array? I've found documentation on how to search for values within arrays, but not on how to search for arrays themselves.

You can simply use, #> (contains) operator.
select * from products;
id | name | tags | created_at | updated_at
----+---------+--------------------------------+----------------------------+----------------------------
3 | T-Shirt | {clothing,summer} | 2017-10-30 05:28:19.394888 | 2017-10-30 05:28:19.394888
4 | Sweater | {clothing,winter,large,hoodie} | 2017-10-30 05:28:38.189589 | 2017-10-30 05:28:38.189589
(2 rows)
select * from products where tags #> '{large, hoodie}';
id | name | tags | created_at | updated_at
----+---------+--------------------------------+----------------------------+----------------------------
4 | Sweater | {clothing,winter,large,hoodie} | 2017-10-30 05:28:38.189589 | 2017-10-30 05:28:38.189589
(1 row)
Or, as an AR query,
2.3.1 :002 > Product.where("tags #> '{large, hoodie}'")
Product Load (0.4ms) SELECT "products".* FROM "products" WHERE (tags #> '{large, hoodie}')
=> #<ActiveRecord::Relation [#<Product id: 4, name: "Sweater", tags: ["clothing", "winter", "large", "hoodie"], created_at: "2017-10-30 05:28:38", updated_at: "2017-10-30 05:28:38">]>

Okay, as you are using postgresql, you can use gem pg_search.
Add search scope in model:
include PgSearch
pg_search_scope :search_on_text_columns,
against: %i(name),
using: { tsearch: { prefix: true } }
For more details check out the documentation. Cheers!

Related

How to expand on result of first query?

This is my first time using Postgres and I would like to write a query that expands on the elements in an array. The example is as follows.
I have some object in a table, say
+-----+------+----+
| 123 | john | AZ |
+-----+------+----+
| 456 | carl | CA |
+-----+------+----+
Another table has an object that contains an array of user ids.
+-----+-----------+
| 999 | {123,456} |
+-----+-----------+
Given the two case classes
case class User(userId: Int, name: String, country: String)
case class Group(groupId: Int, users: List[User])
I would love to write a function with this signature:
def getGroupById(groupId: Int): Future[Group] // or Future[Option[Group]]
so that
getGroupById(999) ---> Group(999, List(User(123, john, AZ), User(456, carl, CA))
For the time being I am doing it the 'brute force' way:
obtain group object with user ids
---> Future.sequence(query each user id)
---> map to desired final object
But, could I achieve this without application logic, in one single query?
I am using the slick-pg extensions for Slick to manipulate arrays in Postgres.

Is it possible to UNNEST an array in BigQuery so that the nested data in split into columns by a key value?

Let's say I have some data in BigQuery which includes a nested array of objects like so:
{
"name" : "Bob",
"age": "24",
"customFields": [
{
"index": "1",
"value": "1.98"
},
{
"index": "2",
"value": "Nintendo"
},
{
"index": "3",
"value": "Yellow"
}
]
}
I've only been able to unnest this data so that the "index" and "value" fields are columns:
+------+-----+-------+----------+
| name | age | index | value |
+------+-----+-------+----------+
| Bob | 24 | 1 | 1.98 |
| Bob | 24 | 2 | Nintendo |
| Bob | 24 | 3 | Yellow |
+------+-----+-------+----------+
In most cases this would be the desired output, but as the data I'm using refers to Google Analytics custom dimensions I require something a bit more complex. I'm trying to get the index value to be used in the name of the column the data appears in, like so:
+------+-----+---------+----------+---------+
| name | age | index_1 | index_2 | index_3 |
+------+-----+---------+----------+---------+
| Bob | 24 | 1.98 | Nintendo | Yellow |
+------+-----+---------+----------+---------+
Is this possible? What would be the SQL query required to generate this output? It should use the "index" value in he column name, as the output won't be in the ordered "1,2,3,..." all the time.
What you are describing is often referred to as a pivot table - a transformation where values are used as columns. SQL doesn't generally support this as SQL is designed around the concept of having a fixed schema while pivot table requires dynamic schemas.
However if you have a fixed set of index columns you can emulate it with something like:
SELECT
name,
age,
ARRAY(SELECT value FROM UNNEST(customFields) WHERE index="1")[SAFE_OFFSET(0)] AS index_1,
ARRAY(SELECT value FROM UNNEST(customFields) WHERE index="2")[SAFE_OFFSET(0)] AS index_2,
ARRAY(SELECT value FROM UNNEST(customFields) WHERE index="3")[SAFE_OFFSET(0)] AS index_3
FROM your_table;
What this does is specifically define columns for each index that picks out the right values from the customFields array.

Lucene Query - AND operator failing in Azure Search?

I have a search index of sandwiches. The index has three fields: id, meat, and bread. Each field is an Edm.String. In this index, here is a subset of my data:
ID | Meat | Bread
-----------------------
1 | Ham | White
2 | Turkey | Hoagie
3 | Tuna | Wheat
4 | Roast Beef | White
5 | Ham | Wheat
6 | Roast Beef | Rye
7 | Turkey | Wheat
I need to write a query that returns all ham or turkey sandwiches on wheat bread. In an attempt to do this, I've created the following:
{
"search":"(meat:(Ham|Turkey) AND bread:\"Wheat\")",
"searchMode":"all",
"select":"id,meat,bread"
}
When I run this query, I'm not seeing any results. What am I missing? What am I doing wrong? I'm trying to understand full queries. Do field-level queries support the phrase operator? I'm not sure what I'm doing wrong.
You need to use "queryType": "full" to request the Lucene syntax. See an example on MSDN.
That said, what you're trying to accomplish is easier and more efficiently done using filters. Assuming you make the relevant fields in your index filterable, you can use the following filter expression for your example: $filter=(meat eq 'Ham' or meat eq 'Turkey') and bread eq 'Wheat'. For more on filters, see this article. Hope this helps!

SQLAlchemy getting label names out from columns

I want to use the same labels from a SQLAlchemy table, to re-aggregate some data (e.g. I want to iterate through mytable.c to get the column names exactly).
I have some spending data that looks like the following:
| name | region | date | spending |
| John | A | .... | 123 |
| Jack | A | .... | 20 |
| Jill | B | .... | 240 |
I'm then passing it to an existing function we have, that aggregates spending over 2 periods (using a case statement) and groups by region:
grouped table:
| Region | Total (this period) | Total (last period) |
| A | 3048 | 1034 |
| B | 2058 | 900 |
The function returns a SQLAlchemy query object that I can then use subquery() on to re-query e.g.:
subquery = get_aggregated_data(original_table)
region_A_results = session.query(subquery).filter(subquery.c.region = 'A')
I want to then re-aggregate this subquery (summing every column that can be summed, replacing the region column with a string 'other'.
The problem is, if I iterate through subquery.c, I get labels that look like:
anon_1.region
anon_1.sum_this_period
anon_1.sum_last_period
Is there a way to get the textual label from a set of column objects, without the anon_1. prefix? Especially since I feel that the prefix may change depending on how SQLAlchemy decides to generate the query.
Split the name string and take the second part, and if you want to prepare for the chance that the name is not prefixed by the table name, put the code in a try - except block:
for col in subquery.c:
try:
print(col.name.split('.')[1])
except IndexError:
print(col.name)
Also, the result proxy (region_A_results) has a method keys which returns an a list of column names. Again, if you don't need the table names, you can easily get rid of them.

Extract data from one field into another in mysql

I have an old table which has a column like this
1 | McDonalds (Main Street)
2 | McDonalds (1st Ave)
3 | The Goose
4 | BurgerKing (Central Gardes)
...
I want to match the venues like ' %(%)' and then extract the content in the brackets to a second field
to result in
1 | McDonalds | Main Street
2 | McDonalds | 1st Ave
3 | The Goose | NULL
4 | BurgerKing| Central Gardes
...
How would one go about this?
MySQL provides string functions for finding characters and extracting substrings. You can also use control flow functions to handle the cases where the venue is not present.
I installed these user defined functions
http://www.mysqludf.org/lib_mysqludf_preg/
Then I could select the "branches" via
SELECT `id`, `name`, preg_capture('/.*?\\((.*)\\)/',`name`,1) AS branch FROM `venues`