RavenDB sorting by Range field - ravendb

I've been playing around with the RavenDB Northwind database, and am having trouble getting orders sorted by Freight.
My index:
Map = orders => from o in orders
select new {
o.Freight
};
IndexSortOptions.Add(x => x.Freight, SortOptions.Double);
Indexes.Add(x => x.Freight, FieldIndexing.NotAnalyzed);
My query:
return sess.Query<Order>("Orders/ByFreight")
.OrderByDescending(x => x.Freight)
.Select(x => x.Freight);
It get the following order back:
[
32.38,
11.61,
65.83,
41.34 ... ]
Which is clearly not correct. In the studio, I can define the order to be by Freight, and it orders just fine. However, if in the studio I specify the range as over Freight_Range, I get these same results back. It appears to me that Raven is selecting the Freight_Range field to sort by rather than the Freight field. Why?

Remove the line starting with Indexes.Add. You don't need to do that.
Use this syntax instead of the IndexSortOptions:
Sort(x=> x.Freight, SortOptions.Double);
Make sure your Freight field is indeed a double as defined on your Order class.

Related

ZF2 how to avoid sql query limit to add quotes in subquery

I'm trying to set up a subquery in ZendFramework 2 and I got an issue with the limit function for a Select object. Whatever I do, numeric value is put between quotes and makes my query fails : I should get LIMIT 1 and instead I get LIMIT '1'.
Seems this is not the first time this issue has been encountered, I saw some have asked about this issue before (like 8 months ago) but without getting any proper answer.
I also saw this issue has been marker as resolved in 2012 (https://github.com/zendframework/zf2/pull/2775) so I really don't understand what's happening there.
Here's my code in ZF2 :
$resultSet = $this->tableGateway->select( function (Select $select) use ($params) {
$sub = new Select();
$sub->from(array('temp' => 'scores'))
->columns(array(new \Zend\Db\Sql\Expression("id AS id")))
->where(array('temp.glitch' => array('None', 'Glitch')))
->where('temp.zone=scores.zone')
->order('temp.multi DESC, temp.score DESC')
->limit(1);
$select->join('players', 'player=players.id', array('player_name' => 'name', 'player_url' => 'name_url'))
->join('countries', 'players.country=countries.id', array('country_name' => 'name', 'country_iso' => 'iso'))
->join('cars', 'car=cars.id', array('car_name' => 'name'), 'left')
->join('zones', 'zone=zones.id', array('zone_name' => 'name'));
$select->where(array('scores.id' => $sub));
$select->order('scores.zone ASC');
print_r($select->getSqlString());
});
This should render the following query (which I get right except LIMIT '1' instead of LIMIT 1) :
SELECT "scores".*, "players"."name" AS "player_name", "players"."name_url" AS "player_url", "countries"."name" AS "country_name", "countries"."iso" AS "country_iso", "cars"."name" AS "car_name", "zones"."name" AS "zone_name"
FROM "scores" INNER JOIN "players" ON "player"="players"."id"
INNER JOIN "countries" ON "players"."country"="countries"."id"
LEFT JOIN "cars" ON "car"="cars"."id"
INNER JOIN "zones" ON "zone"="zones"."id"
WHERE "scores"."id" = (SELECT id AS id FROM "scores" AS "temp" WHERE "temp"."glitch" IN ('None', 'Glitch')
AND temp.zone=scores.zone ORDER BY "temp"."multi" DESC, "temp"."score" DESC LIMIT 1)
ORDER BY "scores"."zone" ASC
Since this doesn't seem to work this way, is there another way I could proceed to get my limit (using Mysql 5 database) ?
EDIT :
Thanks for your help. Finally I figured out a way to get things done the way I want and to remove the quotes by simply remove the subquery construction and to write it directly in the where function :
$select->where('scores.id = (SELECT id FROM scores AS lookup WHERE lookup.zone = scores.zone ORDER BY multi DESC , score DESC LIMIT 1)');
Although I can continue my dev with this, I feel more like using a poor trick to get rid of this issue and so I will let this question unanswered until someone comes with a real solution there.
Anyway there might be no solution at all, since it might be an issue in ZF2 core itself.
Change the line -
$select->where(array('scores.id' => $sub));
with
$select->where(array('scores.id' => new \Zend\Db\Sql\Expression("({$sub->getSqlString($this->tableGateway->adapter->getPlatform())})"));
Try with just above change.
And if it still doesn't work then make changes to the core Select class file located at -
PROJECT_FOLDER/vendor/zendframework/zendframework/library/Zend/Db/Sql/Select.php
Line No. 921 -
Change $sql = $platform->quoteValue($limit); with $sql = $limit;
Line No. 940 -
Change return array($platform->quoteValue($offset)); with return array($offset);
I have come across the issue from github and wondered as why it is still not working with the latest ZF2 files. I know the solution given above doesn't look like the proper one but I had to somehow make it work. I have tried it and it works.
Its only a quick fix before the actual solution comes into picture.

nHibernate QueryOver and JoinQueryOver

I'm reasonably new to nHibernate and I've been trying to write a query. I cant seem to get it right. I have in my model, a "Product" which contains a ICollection of "Component". I need to find all products that contain any components that have a component reference starting with the letter "G". I have tried this:
var matching = session.QueryOver<Product>()
.JoinQueryOver<Component>(p => p.Components)
.Where(c => c.ComponentReference.StartsWith("G")).List();
However I am getting a compile error saying 'Delegate System.Func>> does not take 1 parameter.
There is an overload on JoinQueryOver where I can pass in an Expression>>
So I would have thought my query would work since ICollection implements IEnumerable.
I have tried various other ways using .Cast and JoinAlias but they just seem unecessarily complicated.
Can anyone point out where I am going wrong?
Thanks in advance
I would suggest to use the subquery, in this case. It could look like this
Product product = null;
Component component = null;
// the subselect, returning the Product.ID
// only if the Component.ComponentReference is like G%
var subQuery = QueryOver.Of<Component>(() => component)
.WhereRestrictionOn(() => component.ComponentReference)
.IsLike("G", MatchMode.Start)
.Select(c => c.Product.ID);
// the root Product
var query = session.QueryOver<Product>(() => product)
.WithSubquery
.WhereProperty(() => product.ID)
.In(subQuery);
// and finally the list
var list = query
.List<Product>();
The resulting SQL would be like this:
SELECT product
FROM product
WHERE productId IN (SELECT ProductId FROM Component WHERE ComponenentReferece LIKE 'G%')
And why to use subquery instead of JOIN? because the join would in this case result in Carthesian product. The set of products returned would be multiplied by all the components starting with G%
The subquery, will result in pure, flat set of Products, so we can correctly use paging over it (.Take() .Skip())

CakePHP SQL-query count

I have a problem concerning CakePHP SQL-queries. I need to fetch products from the database where shop_id is given and then count the products. All I need is Product.url and its count.
This will do the trick in plain SQL:
SELECT url,COUNT(*) as count FROM products GROUP BY url ORDER BY count DESC;
This one I used to get all products relating to shops:
$this->Product->find('all', array('conditions' => array('Product.shop_id'=>$id)));
That works correctly, but I need to convert that SQL-query above to CakePHP.
I tried something like this:
$this->Product->find('count', array('conditions' => array('Product.shop_id'=>$id),
'fields'=>array('Product.url','Product.id'),
'order'=>'count DESC',
'group'=>'Product.url'));
That returns only an int. But if I run that SLQ-query presented above in mysql server, I get two columns: url and count. How do I get the same results with CakePHP?
You can try this:
$data = $this->Post->query(
"SELECT COUNT(id),MONTH(created) FROM posts GROUP BY YEAR(created), MONTH(created);"
);
The most easiest way to do this:
$this->Product->query("SELECT url,COUNT(*) as count FROM products GROUP BY url ORDER BY count DESC;");
...at least for me.
Try the following code:
$this->Product->virtualFields['CNT_PRODUCTS'] = 0;
$this->Product->find('count', array('conditions' => array('Product.shop_id' => $id),
'fields' => array('Product.id', 'Product.url', 'count(*) as CNT_PRODUCTS'),
'order' => array('CNT_PRODUCTS' => 'DESC'),
'group' => 'Product.url'));

Need help creating a linq select

I need some help creating an LINQ select, i have a table with some columns in it, but only 2 of interest to this problem.
userid, type
Now this table have many thousands entries, and I only want the top, let’s say 50. So far so good, but the hard part is that there a lot of rows in success that should only be counted as 1.
Example
Type UserId
============
Add 1
Add 1
Add 1
Add 2
I would like this to only be counted as 2 in the limit of rows I am taking out, but I would like all the rows to be outputted still.
Is this possible with a single SQL request, or should I find another way to do this?
Edit: I can add columns to the table, with values if this would solve the problem.
Edit2: Sotred procedures are also an solution
Example 2: This should be counted as 3 rows
Type UserId
============
Add 1
Add 1
Add 2
Add 1
Are you stuck on LINQ?
Add a PK identity.
Order by PK.
Use a DataReader and just count the changes.
Then just stop when the changes count is at your max.
If you are not in a .NET environment then same thing with a cursor.
Since LINQ is deferred you might be able to just order in LINQ and then on a ForEach just exit.
I'm not close to a computer right now so I'm not sure is 100% correct syntax wise, but I believe you're looking for something like this:
data.Select(x => new {x.Type, x.UserId})
.GroupBy(x => x.UserId)
.Take(50);
You could do it with Linq, but it may be a LOT slower than a traditional for loop. One way would be:
data.Where((s, i) => i == 0 ||
!(s.Type == data[i-1].Type && s.UserId == data[i-1].UserId))
That would skip any "duplicate" items that have the same Type and UserID as the "previous" item.
However this ONLY works if data has an indexer (an array or something that implements IList). An IEnumerable or IQueryable would not work. Also, it is almost certainly not translatable to SQL so you'd have to pull ALL of the results and filter in-memory.
If you want to do it in SQL I would try either scanning a cursor and filling a temp table if one of the values change or using a common table expression that included a ROW_NUMBER column, then doing a look-back sub-query similar to the Linq method above:
WITH base AS
(
SELECT
Type,
UserId,
ROW_NUMBER() OVER (ORDER BY ??? ) AS RowNum
FROM Table
)
SELECT b1.Type, b1.UserId
FROM base b1
LEFT JOIN base b2 ON b1.RowNum = b2.RowNum - 1
WHERE (b1.Type <> b2.Type OR b1.UserId <> b2.UserId)
ORDER BY b1.RowNum
You can do this with LINQ, but I think it might be easier to go the "for(each) loop" route...
data.Select((x, i) => new { x.Type, x.UserId, i })
.GroupBy(x => x.Type)
.Select(g => new
{
Type = g.Key,
Items = g
.Select((x, j) => new { x.UserId, i = x.i - j })
})
.SelectMany(g => g.Select(x => new { g.Type, x.UserId, x.i }))
.GroupBy(x => new { x.Type, x.i })
.Take(50);
.SelectMany(g => g.Select(x => new { x.Type, x.UserId }));

How can I retrieve the newest record in each group in a DBIx::Class resultset search?

I'm using group_by in a DBIx::Class resultset search. The result returned for each group is always the row in the group with the lowest id (i.e the oldest row in the group). I'm looking for a way to get the row with the highest id (i.e. the newest row in the group) instead.
The problem is fundamentally the same as this:
Retrieving the last record in each group
...except that I'm using DBIx::Class not raw SQL.
To put the question in context:
I have a table of music reviews
review
------
id
artist_id
album_id
pub_date
...other_columns...
There can be multiple reviews for any given artist_id/album_id.
I want the most recent reviews, in descending date order, with no more than one review per artist_id/album_id.
I tried to do this using:
$schema->resultset('Review')->search(
undef,
{
group_by => [ qw/ artist_id album_id / ],
order_by => { -desc => 'pub_date' },
}
);
This nearly works, but returns the oldest review in each group instead of the newest.
How can I get the newest?
For this to work you are relying on broken database behaviour. You should not be able to select columns from a table when you use group by unless they use an aggregate function (min, max etc.) or are specified in the group by clause.
In MySQL, even the manual admits this is wrong - though it supports it.
What I think you need to do is get the latest dates of the reviews, with max(pub_date):
my $dates = $schema->resultset('Review')->search({},
{
select => ['artist_id', 'album_id', {max => 'pub_date'}],
as => [ qw(artist_id album_id recent_pub_date) ],
group_by => [ qw(artist_id album_id) ],
}
);
Then loop through to get the review:
while (my $review_date = $dates->next) {
my $review = $schema->resultset('Review')->search({
artist_id => $review_date->artist_id,
album_id => $review_date->album_id,
pub_date => $review_date->get_column('recent_pub_date'),
})->first;
}
Yep - it's more queries but it makes sense - what if two reviews are on the same date - how should the DB know which one to return in the select statement?