I´m doing a paging query with a simple filter, it´s working like a charm.
var result = client.Search<MyMetaData>(
x => x.Index("MyIndex")
.Type("MyType")
.QueryString(filtro)
.From(from)
.Size(size)
);
But I need to know the number of results without paging to inform users.
I´m trying to do with the Count method, but without success.
In ES you can use the "Size" field to limit the number of records returned but the "Total" field will always have the correct total on the server even if only 100 records are returned (as with my sample below).
var result = ElasticClient.Search<PackingConfigES>(x =>
x.Size(100)
.MatchAll()
);
var totalResults = result.Total;
Related
I have a list of strings like this
"Users": [
"usrName|Fullname|False|0|False|False",
"usrName|Fullname|False|0|False|False",
"usrName|Fullname|False|0|False|False",
"usrName|Fullname|False|0|False|False",
"usrName|Fullname|False|0|False|False",
]
In my episerver/optimizely code I want to match items. I have written this line of code
searchResult.Filter(x => x.Users.MatchContained(k=> k.Split('|')[3], "0"));
I am trying to get all Users where after splitiing on 'pipe' I get 0 at index 3. I am not getting the result, infact I get an exception.
What am I doing wrong here?
Since you left out A LOT of details these are my assumptions
Users is an IList<string> Users
You are trying to get all Users within that list/array where index 3 equals 0
What we don't know is, among others
Is there more than one page instance that have the Users instance filled?
Anyway, this cannot be solved using any Find API with the current system design. Instead you need to rely on linq to parse the result, but then the Find implementation may not be necessary.
var searchClient = SearchClient.Instance;
var search = searchClient.Search<BlogPage>();
var result = search.GetContentResult();
var usersResult = result
.SelectMany(x => x.Users)
.Where(x => x.Split('|')[3].Equals("0"));
This would create and return an object similar to this, since I'm using SelectMany the array would contain all users throughout the systems where there are matched pages from the search result.
If you for whatever reason would like to keep or see some page properties there are alternative approaches where you construct a new object within a select and remove any object where there where not matched users
var searchClient = SearchClient.Instance;
var search = searchClient.Search<BlogPage>();
var result = search.GetContentResult();
var usersResult = result
.Select(p => new
{
PageName = p.Name,
ContentLink = p.ContentLink.ToString(),
Users = p.Users.Where(x => x.Split('|')[3].Equals("0"))
})
.Where(x => x.Users.Any());
If you like to keep using Find for this kind of implementations you must store the data in a better way than strings with delimiters.
I search lot but not getting any help on random records with mysql.I am using random query to get random data from database with not in with some limit. If database contains 100 records and suppose I am giving 30 as limit with random function and some not in id.My problem is that when it hit first time I am giving not in id as empty and it is giving me 30 records randomly.But for second time except last 30 records i.e(it is in not in variable) it should give me another 30 records from 70 records but it is giving me less than 30 i.e(28,29).This because first it applies random function and limit and then filter it with not in.My query is like below:
$subQuery = $this->select()
->from(array('s' => 'shop'), array('s.shop_id','b.shop_name','b.shop_template_id'))
->order('RAND()')
->where('s.shop_id NOT in (?)', $shop_id)
->limit(30);
$query = $this->select()
->from(array('b' => $subQuery), array('*'))
->join(array('p' => 'product'), 's.shop_id = p.shop_id', array('p.product_price'))
->setIntegrityCheck(false);
$resultRows = $this->fetchAll($query);
Update: I got the problem why it is giving (28,29) records sometime because join query contains product for shop and if some shop is having 0 products it does not get that shop.My question is that irrespective of product how can i get that shop from database.
Your problem is that you join instead of left join. Also there's no need for a sub select. This should work:
$objSelect = $this->select()->setIntegrityCheck(false);
$objSelect->from(
array('s' => 'shop'),
array('s.shop_id','b.shop_name','b.shop_template_id')
);
$objSelect->joinLeft(
array('p' => 'product'),
's.shop_id = p.shop_id',
array('p.product_price')
);
$objSelect->order('RAND()');
$objSelect->limit(30);
$objRowSet = $this->fetchAll($objSelect);
I am trying to create table headers that sort during a back end call in nhibernate. When clicking the header it sends a string indicating what to sort by (ie "Name", "NameDesc") and sending it to the db call.
The db can get quite large so I also have back end filters and pagination built into reduce the size of the retrieved data and therefore the orderby needs to happen before or at the same time as the filters and skip and take to avoid ordering the smaller data. Here is an example of the QueryOver call:
IList<Event> s =
session.QueryOver<Event>(() => #eventAlias)
.Fetch(#event => #event.FiscalYear).Eager
.JoinQueryOver(() => #eventAlias.FiscalYear, () => fyAlias, JoinType.InnerJoin, Restrictions.On(() => fyAlias.Id).IsIn(_years))
.Where(() => !#eventAlias.IsDeleted);
.OrderBy(() => fyAlias.RefCode).Asc
.ThenBy(() => #eventAlias.Name).Asc
.Skip(numberOfRecordsToSkip)
.Take(numberOfRecordsInPage)
.List();
How can I accomplish this?
One way how to achieve this (one of many, because you can also use some fully-typed filter object etc or some query builder) could be like this draft:
Part one and two:
// I. a reference to our query
var query = session.QueryOver<Event>(() => #eventAlias);
// II. join, filter... whatever needed
query
.Fetch(#event => #event.FiscalYear).Eager
var joinQuery = query
.JoinQueryOver(...)
.Where(() => !#eventAlias.IsDeleted)
...
Part three:
// III. Order BY
// Assume we have a list of strings (passed from a UI client)
// here represented by these two values
var sortBy = new List<string> {"Name", "CodeDesc"};
// first, have a reference for the OrderBuilder
IQueryOverOrderBuilder<Event, Event> order = null;
// iterate the list
foreach (var sortProperty in sortBy)
{
// use Desc or Asc?
var useDesc = sortProperty.EndsWith("Desc");
// Clean the property name
var name = useDesc
? sortProperty.Remove(sortProperty.Length - 4, 4)
: sortProperty;
// Build the ORDER
order = order == null
? query.OrderBy(Projections.Property(name))
: query.ThenBy(Projections.Property(name))
;
// use DESC or ASC
query = useDesc ? order.Desc : order.Asc;
}
Finally the results:
// IV. back to query... call the DB and get the result
IList<Event> s = query
.List<Event>();
This draft is ready to do sorting on top of the root query. You can also extend that to be able to add some order statements to joinQuery (e.g. if the string is "FiscalYear.MonthDesc"). The logic would be similar, but built around the joinQuery (see at the part one)
I have a PredicateBuilder expression which returns an IQueryable like so (contrived example):
var predicate = PredicateBuilder.True<CoolEntity>();
predicate = predicate.And(x => x.id > 0);
IQueryable<CoolEntity> results = CoolEntityRepository
.FindBy(predicate)
.OrderByDescending(x => x.id);
I then use this to generate a paginated list containing only the results for a given page using an MVC Index controller for CoolEntity entities like so:
int total = results.Count(); //Used by my controller to show the total number of results
int pageNumber = 2;
int pagesize = 20;
//Only get the entities we need for page 2, where there are 20 results per page
List<CoolEntity> resultList = results
.Skip(pageNumber * pageSize)
.Take(pageSize)
.ToList()
This query ensures that only the relevant results are returned for each page, as there are many thousands of records returned from an SQL database via the repository. It has been working nicely.
The problem is, I now want to be able to find out which of the paginated pages for my Index I would need to return for a particular entity. For example, say one of my CoolEntity objects has an ID of 5 - how could I determine what the value for pageNumber would need to be if I wanted to load the paginated page that that entity would be on for my Index controller?
Specifically, how could I find out that for results, the entity with ID 5 was the 651st row, and consequently use this number to determine pageNumber (i.e. Math.Ceiling(651/pagesize) or something similar)? Is there a better approach to this?
A colleague of mine came up with a solution for this which seems to be very fast.
results
.Select( x => x.id)
.ToList()
.Select((entry, index) => new { ID = entry, Rank = index + 1, PageNumer = ((index+ 1) / pagesize ) + 1 })
.Where(x => x.id== 5)
This found the result from ~100000 records in about 00:00.191 seconds when tested in Linqpad. Please let me know if you have a more efficient way of doing this.
I want to do paging with NHibernate when writing a Linq query. It's easy to do something like this:
return session.Query<Payment>()
.OrderByDescending(payment => payment.Created)
.Skip((page - 1)*pageSize)
.Take(pageSize)
.ToArray();
But with this I don't get any info about the total number of items. And if I just do a simple .Count(), that will generate a new call to the database.
I found this answer which solved it by using future. But it uses Criteria. How can I do this with Linq?
The difficulty with using Futures with LINQ is that operations like Count execute immediately.
As #vandalo found out, Count() after ToFuture() actually runs the Count in memory, which is bad.
The only way to get the count in a future LINQ query is to use GroupBy in an invariant field. A good choice would be something that is already part of your filters (like an "IsActive" property)
Here's an example assuming you have such a property in Payment:
//Create base query. Filters should be specified here.
var query = session.Query<Payment>().Where(x => x.IsActive == 1);
//Create a sorted, paged, future query,
//that will execute together with other statements
var futureResults = query.OrderByDescending(payment => payment.Created)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToFuture();
//Create a Count future query based on the original one.
//The paged query will be sent to the server in the same roundtrip.
var futureCount = query.GroupBy(x => x.IsActive)
.Select(x => x.Count())
.ToFutureValue();
//Get the results.
var results = futureResults.ToArray();
var count = futureCount.Value;
Of course, the alternative is doing two roundtrips, which is not that bad anyway. You can still reuse the original IQueryable, which is useful when you want to do paging in a higher-level layer:
//Create base query. Filters should be specified here.
var query = session.Query<Payment>();
//Create a sorted, paged query,
var pagedQuery = query.OrderByDescending(payment => payment.Created)
.Skip((page - 1) * pageSize)
.Take(pageSize);
//Get the count from the original query
var count = query.Count();
//Get the results.
var results = pagedQuery.ToArray();
Update (2011-02-22): I wrote a blog post about this issue and a much better solution.
The following blog post has an implementation of ToFutureValue that works with LINQ.
http://sessionfactory.blogspot.com.br/2011/02/getting-row-count-with-future-linq.html
It has a small error on the following line that must be changed from this.
var provider = (NhQueryProvider)source.Provider;
To this:
var provider = (INhQueryProvider)source.Provider;
After apply the change you can use que queries in this way:
var query = session.Query<Foo>();
var futureCount = query.ToFutureValue(x => x.Count());
var page = query.Skip(pageIndex * pageSize).Take(pageSize).ToFuture();
var query = Session.QueryOver<Payment>()
.OrderByDescending(payment => payment.Created)
.Skip((page -1 ) * pageSize)
.Take(pageSize)
This is something I just discovered that the Linq to NH handles just fine, the ToRowCountQuery removes take/skip from the query and does a future row count.
var rowCount = query.ToRowCountQuery().FutureValue<int>();
var result = query.Future();
var asArray = result.ToArray();
var count = rowCount.Value();
Ok, it seems it should be working in your case, but I not tested:
return session.QueryOver<Payment>()
.Skip((page - 1) * pageSize)
.Take(pageSize)
.SelectList(r => r.SelectCount(f => f.Id))
.List<object[]>().First();
Test first before upvoting ;)
UPD: sorry, as I understand you now, you need to get Count of all items. Then you need to run the query without paging:
return session.QueryOver<Payment>()
.SelectList(r => r.SelectCount(f => f.Id))
.List<object[]>().First();