How to use SNIPPET function in SphinxQL? - sphinx

I have an index:
--------------------------------------------------
| id | name | folder | tag1 | tag2 | topic |
--------------------------------------------------
| 1 | file 1 | AAA | [1,2] | [3,4] | 1 |
--------------------------------------------------
| 2 | file 2 | BBB | [1] | [4,5] | 1 |
--------------------------------------------------
| 3 | file 3 | AAA | [2] | [4] | 2 |
--------------------------------------------------
What I need to do is to filter files by tag and the following request works fine:
SELECT id, name,
ANY(var=1 FOR var IN tag1) as tag_filter_1,
ANY(var=5 FOR var IN tag2) as tag_filter_2,
GROUP_CONCAT(id) as files
FROM index
WHERE tag_filter_1 = 1 AND tag_filter_2 = 1
GROUP BY topic;
Now I need to modify the query to apply the tag1 filter only for the files from AAA folder and at the same time keep filtering by tag2 from all the folders.
I was thinking about OR condition but it's not supported. Also, the option was to use GROUP_CONCAT(tag1) and then filter the Sphinx results in PHP but tag1 is JSON, not scalar.
I am wondering if it's possible to solve this using SNIPPET or IF function and how. Or any other ideas?

It's maybe a little difficult to see, but can just do...
SELECT id, name,
ANY(var=1 FOR var IN tag1) OR NOT folder='AAA' AS tag_filter_1,
ANY(var=5 FOR var IN tag2) AS tag_filter_2
FROM index WHERE tag_filter_1 = 1 AND tag_filter_2 = 1;
tag_filter_1 becomes tag1 filter applies, OR not AAA folder.
So a document in AAA folder, needs to match the tag1 filter (because can't match the second part)
.. but document in other folders will always match because of OR, so the tag1 filter is ignored.
(there isnt a != string operator, so need to invert the use of = operator :)

Related

Reset column with numeric value that represents the order when destroying a row

I have a table of users that has a column called order that represents the order in they will be elected.
So, for example, the table might look like:
| id | name | order |
|-----|--------|-------|
| 1 | John | 2 |
| 2 | Mike | 0 |
| 3 | Lisa | 1 |
So, say that now Lisa gets destroyed, I would like that in the same transaction that I destroy Lisa, I am able to update the table so the order is still consistent, so the expected result would be:
| id | name | order |
|-----|--------|-------|
| 1 | John | 1 |
| 2 | Mike | 0 |
Or, if Mike were the one to be deleted, the expected result would be:
| id | name | order |
|-----|--------|-------|
| 1 | John | 1 |
| 3 | Lisa | 0 |
How can I do this in PostgreSQL?
If you are just deleting one row, one option uses a cte and the returning clause to then trigger an update
with del as (
delete from mytable where name = 'Lisa'
returning ord
)
update mytable
set ord = ord - 1
from del d
where mytable.ord > d.ord
As a more general approach, I would really recommend trying to renumber the whole table after every delete. This is inefficient, and can get tedious for multi-rows delete.
Instead, you could build a view on top of the table:
create view myview as
select id, name, row_number() over(order by ord) ord
from mytable

Selecting value for the latest two distinct columns

I am trying to do an SQL which will return the latest data value of the two distinct columns of my table.
Currently, I select distinct the values of the column and afterwards, I iterate through the columns to get the distinct values selected before then order and limit to 1. These tags can be any number and may not always be posted together (one time only tag 1 can be posted; whereas other times 1, 2, 3 can).
Although it gives the expected outcome, this seems to be inefficient in a lot of ways, and because I don't have enough SQL experience, this was so far the only way I found of performing the task...
--------------------------------------------------
| name | tag | timestamp | data |
--------------------------------------------------
| aa | 1 | 566 | 4659 |
--------------------------------------------------
| ab | 2 | 567 | 4879 |
--------------------------------------------------
| ac | 3 | 568 | 1346 |
--------------------------------------------------
| ad | 1 | 789 | 3164 |
--------------------------------------------------
| ae | 2 | 789 | 1024 |
--------------------------------------------------
| af | 3 | 790 | 3346 |
--------------------------------------------------
Therefore the expected outcome is {3164, 1024, 3346}
Currently what I'm doing is:
"select distinct tag from table"
Then I store all the distinct tag values programmatically and iterate programmatically through these values using
"select data from table where '"+ tags[i] +"' in (tag) order by timestamp desc limit 1"
Thanks,
This comes close, but beware if you have two rows with the same tag share a maximum timestamp you will get duplicates in the result set
select data from table
join (select tag, max(timestamp) maxtimestamp from table t1 group by tag) as latesttags
on table.tag = latesttags.tag and table.timestamp = latesttags.maxtimestamp

How to aggregate Postgres table so that ID is unique and column values are collected in array?

I'm not sure how to call what I'm trying to do, so trying to look it up didn't work very well. I would like to aggregate my table based on one column and have all the rows from another column collapsed into an array by unique ID.
| ID | some_other_value |
-------------------------
| 1 | A |
| 1 | B |
| 2 | C |
| .. | ... |
To return
| ID | values_array |
-------------------------
| 1 | {A, B} |
| 2 | {C} |
Sorry for the bad explanation, I'm really lacking the vocabulary here. Any help with writing a query that achieves what's in the example would be very much appreciated.
Try the following.
select id, array_agg(some_other_value order by some_other_value ) as values_array from <yourTableName> group by id
You can also check here.
See Aggregate Functions documentation.
SELECT
id,
array_agg(some_other_value)
FROM
the_table
GROUP BY
id;

Eloquent: Distinct values and counts from relations

I have a DB structure as follows:
fashion_item
==============
| id | name |
|------------|
| 1 | item1 |
|------------|
| 2 | item2 |
--------------
fashion_colour
===============
| id | name |
|-------------|
| 1 | red |
|-------------|
| 2 | white |
|-------------|
| 3 | green |
---------------
| 4 | black |
---------------
fashion_color_fashion_item
======================================
| fashion_item_id | fashion_color_id |
|------------------------------------|
| 1 | 1 |
|------------------------------------|
| 1 | 2 |
|------------------------------------|
| 1 | 3 |
|------------------------------------|
| 2 | 2 |
|------------------------------------|
| 2 | 3 |
--------------------------------------
The fashion_color_fashion_item table is a join table for a many to many relationship between fashion_item and fashion_color.
Using Eloquent, I would like to retrieve a list of results from fashion_item (based on other criteria) then get a distinct list of fashion_colour id's from the results, with a count.
I need to end up with a value like the following, though I'm willing to transform the relevant data from another structure.
[ 1 => 1, 2 => 2, 3 => 2, 4 => 0 ]
In this format, there is a key which reflects a fashion_color.id, and a value which represents the number of times the colour is referenced by a row from the result set.
fashion_colour.id's with no count result can be null, 0 or simply not present.
I have the correct relationships setup between the tables and I can return results using all of the regular methods, including eager loading the colour data.
I've been able to achieve a similar result on direct belongs to relationships by grouping the results based on the foreign key in the table and counting the array. This won't work for many-to-many relationships.
e.g.
$silhouetteFilterList = array();
$results = FashionItem::(where clauses, etc...)->get();
$silhouettes = $results->groupBy('fashion_silhouette_id')->all();
foreach ($silhouettes as $key => $value) {
$silhouetteFilterList[$key] = count($value);
}
P.S. We're currently using Eloquent 4.1 because we need PHP 5.3 compatibility, we're hoping to move on soon. Comments regarding the antiquated nature of either PHP5.3 or Eloquent 4.1 will not be welcome :p
We are using Eloquent but not Laravel.
Try eager loading the relationship:
$collection = $items->with('colors')->get();
Each item in the collection should now have a colors variable that represents an array of colors for that particular item.
Since this is a general Laravel Collection, you can use collection and array methods to get it in the format you like.

How do I return multiple documents that share indexes efficiently?

I'm indexing some data using Sphinx. I have objects that are categorised and the categories have a heirarchy. My basic table structure is as follows:
Objects
| id | name |
| 1 | ABC |
| 2 | DEF |
...
Categories
| id | name | parent_id |
| 1 | My Category | 0 |
| 2 | A Child | 1 |
| 3 | Another Child | 1 |
...
Object_Categories
| object_id | category_id |
| 1 | 2 |
| 2 | 3 |
...
My config currently is:
sql_query = SELECT categories.id, objects.name, parent_id FROM categories \
LEFT JOIN object_categories ON categories.id = object_categories.category_id \
LEFT JOIN objects ON objects.id = object_categories.object_id
sql_attr_uint = parent_id
This returns category IDs for any categories that contain objects that match my search, but I need to make an adjustment to get objects in that category or any of it's children.
Obviously, I could UNION this query with another that gets ID from matched categories parents, and so on (it could be up to 4 or 5 levels deep), but this seems hugely inefficient. Is there a way to return multiple document IDs in the first field, or to avoid repeated needless indexing?
I'm a Sphinx noob, so I'm not sure how to approach the problem.
See
http://www.sitepoint.com/hierarchical-data-database/
its talking about a database, but the same system works equally well within sphinx. It can take a while to get your head around, but its well worth mastering (IMHO!).
(ie add the left/right columns to the database, and then include them as attributes in the sphinx index)