How to find position of result in couchbase query? - couchbase

I am looking for a way to find the position of the first document (given some kind of criteria like userId="user123") for an ordered N1QL query, so something like this:
SELECT ARRAY_POSITION(allPoints, "user123")
LET allPoints = (SELECT userId from stuff WHERE ... ORDER BY points DESC, userId ASC)
However, it doesn't work since the subquery "allPoints" returns an array of objects (each having just one attribute which is 'userId')

Got the solution 2 minutes after posting :)
I was pretty close, it works like this:
SELECT ARRAY_POSITION(allPoints, {"userId":"user123"})
LET allPoints = (SELECT userId from stuff WHERE ... ORDER BY points DESC, userId ASC)

You can also use the following expression to find position in points array when lang 10.0 and lat 20.1 (WHEN clause can have any type of condition)
(FIRST pos FOR pos:v IN points WHEN v.lang = 10.0 AND v.lat = 20.1 END)

Related

Select the nearest point for each row in table

I use this to find the nearest point
SELECT
id,
ST_Distance(
POINT(52.760667210533,-7.22646337599035),
geo_point
) as distance
from Points
order by distance limit 1
I have a temp table TempPoints with all my candidate points and I want to normalise them onto OSM nodes, but there's lots, so I need a single query to resolve them all in one call. UNION wont let me use order by, and my DB raw query interface wont let me just fire a series of queries separated by ';'.
The temp table has lat and lon but can just as easily have a POINT. How can I go
select id,NearestTo(TempPoint.geo_point,Points) from TempPoints;
EDIT: I can parenthesise each select in my large union query, which solves my issue.
I would still like to be able to join on nearest row.
This might work for you:
SELECT t.id as tid, p.id as pid, p.geo_point
FROM TempPoint t
JOIN Points p ON p.id = (
SELECT p1.id
FROM Points p1
ORDER BY ST_Distance(p1.geo_point, t.geo_point)
LIMIT 1
)
My solution is to issue a series of queries, one for each row, and bind them together with a UNION. The mysql stack will blow eventually so you need to do them in blocks, but 1000 is OK on a default install.
You have to parenthesize the queries as they include an order by. Some points may fail so I label them all with a literal line_no sequence so you can edit and filter the originals. You also need to restrict the query with a
WHERE Contains(<polygon>,point)
clause, else it will try and sort the whole table, where polygon is a bounding box you have to cook up with GEOMFROMTEXT() and POLYGON(). And of course you need a special spatial index on the column!. Here's some code
var SMALL=0.001
var=query=points
.map(function(point){
var bottom=point.lat+SMALL
var top=point.lat-SMALL
var left=point.lon-SMALL
var right=point.lon+SMALL
var polygon=[
[bottom,left],
[top,left],
[top,right],
[bottom,right],
[bottom,left]
]
polygon="POLYGON(("+polygon.map(function(point){
return point.join(' ')
})
.join(",")+"))"
point.line_no=line_no++
return "(SELECT "+point.line_no+" as line_no,id, ST_Distance(POINT("+
point.lat+","+point.lon+
"),geo_point) as distance"+
" from Points "+
" WHERE Contains(GeomFromText('"+polygon+"'),geo_point) "+
" order by distance limit 1) "
})
.join(" UNION ")+" order by line_no"
return sequelize.query(query)

How to order query for searching where value = or is LIKE

I have a query that searches for stock numbers based on a user's input. I am trying to find the top 5 items that most closely match the input. If a stock number matches exactly, show that item first.
SELECT
ID, Stock
FROM `store_items`
WHERE Stock = '$query' OR Stock LIKE '%$query%' LIMIT 5
So, if in my database I have the following stock #'s:
MC-10
MC-11
MC-12
MC-100
MC-102
MC-103
And I search for MC-10, the exact result should show first, followed by other matches that it is LIKE:
MC-10
MC-100
MC-102
MC-103
How do I do the ORDER BY part of this query to make that happen?
You can try like
ORDER BY CAST(SUBSTRING(Stock,LOCATE('-', Stock) +1, LENGTH(Stock) - LOCATE('-', Stock)) AS INT);
Use
ORDER BY Stock = '$query' DESC
TRUE is treated as 1 and FALSE is 0, so this will order the equal row first.
I would order the result by Levenshtein distance to do this, but it may not be fast.
This place I grabbed an implementation of Levenshtein in MySQL from is: https://jonlabelle.com/snippets/view/sql/mysql-levenshtein-distance-algorithm
(I will not plagiarize for the answer as google will find you many examples)
Here is a fiddle example: http://sqlfiddle.com/#!9/099a4/1 so your query will look like:
Select *, levenshtein_ratio('MC-10', productName) From ForgeRock WHERE productName like '%MC-10%'
ORDER BY levenshtein_ratio('MC-10', productName) desc, productName ASC

assign sequential number to each set of duplicate records

I've been searching for an easy solution to a pretty trivial problem. I have an huge set of records (~120,000) that I need to screen for duplicates, assign a sequential number to each set of duplicates, like Assign# below:
Eventually, I am trying to achieve this:
I use P1, P2, and P3 fields as a set of sort parameters in query (ascending/descending) to determine the best/top Name for each set of identical NCBI hits.
I tried a lot of things already and my main problem is that access freezes half way through and I don't really know if the script is functional.
FROM [sortquery]
WHERE ((([sortquery].Name) In
(
SELECT TOP 1 [sortquery].Name
FROM [sortquery] AS Dupe
WHERE Dupe.NCBI=[sortquery].NCBI
ORDER BY Dupe.NCBI
)))
ORDER BY [sortquery].NCBI;
I am open to any suggestion and corrections! Thanks for any help =)
The traditional method is to count:
SELECT
*,
(Select Count(*)
From Sortquery As S
Where S.NCBI = Sortquery.NCBI
And S.P1 * 1000 + S.P3 >= Sortquery.P1 * 1000 + Sortquery.P3) As [Assign#]
FROM
[sortquery]
ORDER BY
NCBI Asc,
P1 Desc,
P3 Desc,
[Name] Asc,
[Assign#] Asc

SQL: user variable increment for each rows

EDIT : my question is not clear, so I've reformulated it here : Order sql result by occurrence of a set of keywords in a string
I'm improving my search system for my website. I'm trying to use and increment variables in sql request, like that...
SET #titlematch = 0;
SELECT *,
CASE
when title like '%apple%' then (SET #titlematch = #titlematch+1)
when title like '%orange%' then (SET #titlematch = #titlematch+1)
when title like '%other_keyword_searched%' then (SET #titlematch = #titlematch+1)
(...)
END,
(...)
FROM pages
(...)
ORDER by #titlematch desc
In fact, titlematch should be incremented each time that a keyword is in the title. If there's "apple" and "orange" in the title, titlematch should be equal to 2.
But actually, it doesn't work...
(sorry for my english)
I think it fails because it must handle all the data,if title like someWordYouDontAcccountFor it will fail.You must account for all possible cases or use else.
In response to your comment (Yes, always), I rewrite your query in this way:
SELECT *, (select count(*) from pages p2 where p1.field_date < p2.field_date) as pos
(...)
FROM pages p1
(...)
ORDER by (select count(*) from pages p2 where p1.field_date < p2.field_date) desc
In this way you count every rows before the actual (I've based my count on ipotetic field_date but if you want you can change your condition), so you have an incremental value for each row, and finally, I add this condition in order by clause.
Tell me if it's OK

MYSQL GROUP_CONCAT and IN

I have a little query, it goes like this:
It's slightly more complex than it looks, the only issue is using the output of one subquery as the parameter for an IN clause to generate another. It works to some degree - but it only provides the results from the first id in the "IN" clause. Oddly, if I manually insert the record ids "00003,00004,00005" it does give the proper results.
What I am seeking to do is get second level many to many relationship - basically tour_stops have items, which in turn have images. I am trying to get all the images from all the items to be in a JSON string as 'item_images'. As stated, it runs quickly, but only returns the images from the first related item.
SELECT DISTINCT
tour_stops.record_id,
(SELECT
GROUP_CONCAT( item.record_id ) AS in_item_ids
FROM tour_stop_item
LEFT OUTER JOIN item
ON item.record_id = tour_stop_item.item_id
WHERE tour_stop_item.tour_stops_id = tour_stops.record_id
GROUP BY tour_stops.record_id
) AS rel_items,
(SELECT
CONCAT('[ ',
GROUP_CONCAT(
CONCAT('{ \"record_id\" : \"',record_id,'\",
\"photo_credit\" : \"',photo_credit,'\" }')
)
,' ]')
FROM images
WHERE
images.attached_to IN(rel_items) AND
images.attached_table = 'item'
ORDER BY img_order ASC) AS item_images
FROM tour_stops
WHERE
tour_stops.attached_to_tour = $record_id
ORDER BY tour_stops.stop_order ASC
Both of these below answers I tried, but it did not help. The second example (placing the entire first subquery inside he "IN" statement) not only produced the same results I am already getting, but also increased query time exponentially.
EDIT: I replaced my IN statement with
IN(SELECT item_id FROM tour_stop_item WHERE tour_stops_id = tour_stops.record_id)
and it works, but it brutally slow now. Assuming I have everything indexed correctly, is this the best way to do it?
using group_concat in PHPMYADMIN will show the result as [BLOB - 3B]
GROUP_CONCAT in IN Subquery
Any insights are appreciated. Thanks
I am surprised that you can use rel_items in the subquery.
You might try:
concat(',', images.attached_to, ',') like concat('%,', rel_items, ',%') and
This may or may not be faster. The original version was fast presumably because there are no matches.
Or, you can try to change your in clause. Sometimes, these are poorly optimized:
exists (select 1
from tour_stop_item
where tour_stops_id = tour_stops.record_id and images.attached_to = item_id
)
And then be sure you have an index on tour_stop_item(tour_stops_id, item_id).