Is there a way to sort a Doctrine 2 EntityRepository Object with specific criteria or do I have to use a DQL query if I want to pass specific sorting parameters?
You can return an ordered set of elements by passing a second argument into the find___ methods, this argument must be an associative array where the key is the name of the field you want to order and the value is either 'ASC' or 'DESC'.
// query for one product matching be name and price
$product = $repository->findOneBy(array('name' => 'foo', 'price' => 19.99));
// query for all products matching the name, ordered by price
$product = $repository->findBy(
array('name' => 'foo'),
array('price' => 'ASC')
);
http://symfony.com/doc/current/book/doctrine.html#fetching-objects-from-the-database
Of course, if you want a most sophisticated behavior you should implement a custom repository.
Related
Let's assume I have entity User with property country. country is just a string and many users can have setted the same country. Then how to get list of all unique countries of all users? I'm using Symfony2.8 with Doctrine.
you need to use the DISTINCT clause: https://www.w3schools.com/sql/sql_distinct.asp
Here is how you can do it from a controller :
$qb = $em->getRepository("MyBundle:Country")->createQueryBuilder("c");
$countries = $qb->select("c")
->distinct(true)
->getQuery()
->getResult();
one liner:
$countries = $em->getRepository("MyBundle:Country")->findBy(array('distinct' => true));
However if i were you i would create a country entity wich is unique and make ManyToOne relationship between User and Country.that would be a much cleaner solution IMO
Using Sitecore 8.2 with MVC.
I'm trying to implement the search functionality in a MVC view. (with a textbox and submit button)
There is a folder in the content tree called Books which has a list of items. Each item will have these fields - Title, Author, Price
When user searches for a term, it will be checked for a match with any of the 3 fields of the item and return the results.
This method is not working as it returns null Item.
public PartialViewResult GetSearchBooks(string txtSearch)
{
string index = string.Format("sitecore_{0}_index", Sitecore.Context.Database.Name);
List<SearchResultItem> query;
List<Item> matches = new List<Item>();
using (var context = ContentSearchManager.GetIndex(index).CreateSearchContext())
{
query = context.GetQueryable<SearchResultItem>()
.Where(p => p.Path.StartsWith("/sitecore/content/Book")).ToList();
}
foreach(SearchResultItem sritem in query)
{
Item item = sritem.GetItem(); //item is null here
if(item.Fields["Title"].Value.Contains(txtSearch) ||
item.Fields["Title"].Value.Contains(txtSearch) ||
item.Fields["Title"].Value.Contains(txtSearch))
matches.Add(item);
}
return(matches);
}
Is it the right approach. If not please suggest one.
Paths
Avoid querying the path like this:
context.GetQueryable<SearchResultItem>()
.Where(p => p.Path.StartsWith("/sitecore/content/Book"));
Instead use
context.GetQueryable<SearchResultItem>()
.Where(p => p.Paths.Contains(idOfBookFolderItem));
For more info on why, see http://blog.paulgeorge.co.uk/2015/05/29/sitecore-contentsearch-api-filtering-search-on-folder-path/
Approach
You need to hand the entire query to the search api in one go.
List<SearchResultItem> matches;
using (var context = ContentSearchManager.GetIndex(indexName).CreateSearchContext())
{
var predicate = PredicateBuilder.True<SearchResultItem>();
// must have this (.and)
predicate = predicate.And(p => p.Paths.Contains(bookFolderItem.ID));
// must have this (.and)
predicate = predicate.And(p => p.Name == searchTerm);
matches = context.GetQueryable<SearchResultItem>().Where(predicate).ToList();
}
This returns SearchResultItems not Items. If you need the item, just call GetItem.
Matches[i].GetItem()
Null items
This may indicate that your index is out of sync with the database. Try re-indexing from control panel, or in the case of the web database, REpublish the expected content.
Searching template fields
This just searches against the item name. You're limited to being able to specify the generic fields in SearchResultItem class. If you want to search specific fields on items, you can inherit from SearchResultItem and add those fields.
public class BookSearchResultItem : SearchResultItem
{
[IndexField("Book Title")]
public string BookTitle { get; set; }
[IndexField("Book Author")]
public string BookAuthor { get; set; }
}
You can then pass this into the query and search on those fields
List<BookSearchResultItem> matches;
using (var context = ContentSearchManager.GetIndex(indexName).CreateSearchContext())
{
var predicate = PredicateBuilder.True<BookSearchResultItem>();
// must have this (.and)
predicate = predicate.And(p => p.Paths.Contains(bookFolderItem.ID));
// must have this (.and)
predicate = predicate.And(
PredicateBuilder.False<BookSearchResultItem>() // in any of these fields
.Or(p => p.BookTitle == searchTerm)
.Or(p => p.BookAuthor == searchTerm)
.Or(p => p.Name == searchTerm));
matches = context.GetQueryable<BookSearchResultItem>().Where(predicate).ToList();
}
Searching all 'content'
If you find that having to specify the explicit fields is an unwanted hassle or you are performing searches across different templates with different fields, you can instead use the special computed 'content' field which combines all the text data from an item into one indexed field. So instead of the original query which did this
predicate = predicate.And(p => p.Name == searchTerm);
You can instead do just use
predicate = predicate.And(p => p.Content == searchTerm);
Which will find results where the searchterm exists in any field on the item.
First, did you check "query" contains any result?
I would suggest performing the following search query:
query = context.GetQueryable<SearchResultItem>()
.Where(p => p.TemplateId == yourBookItemTemplateID &&
(p.Fields["Title"].Value.Contains(txtSearch) ||
p.Fields["Author"].Value.Contains(txtSearch) ||
p.Fields["Price"].Value.Contains(txtSearch));
return query.Select(x => x.GetItem());
i would not suggest this approach to use. Let me explain why, or what you can do better.
First create your own Sitecore index and do not simply use the default master or web index. If you do that, you can safe the following line of code .Where(p => p.Path.StartsWith("/sitecore/content/Book")).ToList();, cause in a custom index you can simply restrict, what exactly is crawled.
Second you should never access the Sitecore item out of the search results. Reasons for that is the performance. Item item = sritem.GetItem(); You use a search, because its a performant way to access a huge amount of data. When you now access for every result the Sitecore item from the database, you lose your benefit of using a search.
You should simply use the Result Type, in your case the basic SearchResultItem. At the End of your filtering you should call something like var results = query.GetResults(); instead of accessing the items directly.
Here I found a simple example of a sitecore search, with custom index and without accessing the items directly, maybe this helps you.
http://www.mattburkedev.com/sitecore-7-contentsearch-tips/
Now to your problem.
Did you debug the search and looked into the rest of the fields of sritem? Are they all filled? If i remember correctly there is a property which stores the itemId, to retrieve the item with GetItem(). Maybe you could give us the values of the property while trying to retrieve the item.
Sometimes when the index is out of date, the returned search items may no longer exist in your content tree, So rebuild the index and try the search again,
Couple of enhancements that you can apply to your search:
As mentioned in Christian answer you can create index for just your Books tree, which means to set the root of the index to the Books root item.Web index usually used for full site content search.
Instead of getting all books items then go through all items; you can use predicates instead; even after you create the new index use the predicates to get the desired items only.
Also if your site is multilingual add a predicate to filter the required language else you will get multiple versions of the same item.
I have a IQueryable<T> object as search results object.
I apply the filtering and sorting on this search object.
Before I call the GetResults(), I want to order the results based on one of the field's (Fieldname - Priority) value. So for all the items in the IQueryable<T> object, I want to order them desc by Priority field, so all the items which has a value for that field stay at the top and the rest are at the bottom.
I have the fieldmap entry for Priority field.
search.OrderByDescending(i => !string.IsNullOrEmpty(i.GetItem().GetFieldValue("Priority")))
The above command doesn't work. Apparently, I can't use Sitecore extension methods with IQueryable?
If I convert search.ToList(). Do the ordering and then convert it back to AsQueryable(), I get the following error:
There is no method 'GetResults' on type 'Sitecore.ContentSearch.Linq.QueryableExtensions'
that matches the specified arguments
Is there a neat and quick way to get around this?
Cheers
I think you just need to add your field to your SearchResultItem and mark it as an int. I am making the assumption that the field is an int. Make a custom class that inherits SearchResultItem.
public class CustomSearchResultItem : SearchResultItem
{
[IndexField("Priority")]
public int Priority { get; set; }
}
Then use it in your search. Finally order by it.
using (var context = ContentSearchManager.GetIndex("sitecore_master_index").CreateSearchContext())
{
var results = context.GetQueryable<CustomSearchResultItem>().Where(prod => prod.Content.Contains("search box text").OrderByDescending(t => t.Priority);
}
Some data found here.
http://www.sitecore.net/learn/blogs/technical-blogs/sitecore-7-development-team/posts/2013/10/sorting-and-ordering-results.aspx
You can order search results using multiple fields by using the OrderByDescending combined with ThenByDescending. So you would need to order by Priority and then by [Name|Date|Whatever].
I want to order them desc by Priority field, so all the items which has a value for that field stay at the top and the rest are at the bottom.
I sort them first on the criteria chosen by the user - like Name, Date created etc. Once I get the results back, I need to order them by priority field
You are conflicting yourself in the questions and comments. If you want the results with priority first and then by user selected results then the following will work:
query = dataQuery.OrderByDescending(i => i.Title).ThenByDescending(i => i["Priority"]);
var results = query.GetResults().Hits.Select(h => h.Document);
There was a bug in earlier version of Sitecore which meant that the ThenBy clause will be added before the OrderBy clause hence it is added in reverse above. You may want to check if this is fixed in the current version. If so simply change your query to:
query = dataQuery.OrderByDescending(i => i["Priority"]).ThenByDescending(i => i.Title);
You don't have to add the field to your SearchResultItem if you just want to order by it, only if you need the actual value of that field returned to as well.
If you need to order by a custom user supplied value then you can pass in i => i["whatever-field-the-user-has-selected"] instead of i.Title.
You can find more info in this blog post.
I am using Pig 0.8.1. I am somewhat new to Pig but I know there must be a reasonable and re-usable solution for how I want to work with my tuples. I have the following format (similar to triples):
Schema: (uuid, key, val)
Data:
(id1, 'name', 'Corey')
(id1, 'location', 'USA')
(id1, 'carsOwned', 5)
(id2, 'name', 'Paul')
(id2, 'location', 'CANADA')
(id2, 'carsOwned', 10)
The reason I'm representing this data in triples is because it's possible to have multi-valued keys, so pushing the data into a map is out of the question.
What I need to be able to do is find the ids, names and locations of the people with the top 10 cars owned. I'd like it if my output format could be this when sorted in descending order:
Schema: (uuid, name, location, carsOwned)
Data:
(id2, 'Paul', 'CANADA', 10)
(id1, 'Corey', 'USA', 5)
I have tried filtering my input into 3 different aliases (one where key == 'name', one where key == 'location' and one where key == 'carsOwned') so that I can use JOIN and bring them back into one tuple, but it appears that Pig ends up loading from the inputFormat 3 times instead of one. Maybe I'm doing that wrong?
I also tried grouping by id but then I can't seem to find a reasonable way to work with the bag of the actual triple key/values since they all have the exact same schema.
What I really want is to group by the id field and then flatten each of the keys but rename the alias to the actual name of the key.
Any ideas? Thanks in advance!
This solution is a bit sloppy, because your data is not organized in a way that Pig is really set up for -- i.e., conceptually each id show be a row key, with the fields named by what you have in the second column. But this can still be done, as long as your data is all reasonable. If you erroneously wind up with multiple rows with the same id and field name, but different values, this will get complicated.
Use a nested foreach to pick out the values for the three fields you're interested in.
keyedByID =
/* Gather the rows by ID, then process each one in turn */
FOREACH (GROUP Data BY id) {
/* Pull out the fields you want. If you have duplicate rows,
you'll need to use a LIMIT statement to ensure just a single record */
name = FILTER Data BY field == 'name';
location = FILTER Data BY field == 'location';
carsOwned = FILTER Data BY field == 'carsOwned';
GENERATE
/* Output each field you want. You'll need to use FLATTEN since
the things created above in the nested foreach are bags. */
group AS id,
FLATTEN(name) AS name,
FLATTEN(locatioN) AS location,
FLATTEN(carsOwned) AS carsOwned;
};
Now you've got a relation that puts all the information for an ID on a single row, and you can do with it whatever you want. For example, you said wanted to pull out the top 10 car owners:
ordered = ORDER keyedByID BY carsOwned DESC;
top10 = LIMIT ordered 10;
I'm struggling getting my head around the Django's ORM. What I want to do is get a list of distinct values within a field on my table .... the equivalent of one of the following:
SELECT DISTINCT myfieldname FROM mytable
(or alternatively)
SELECT myfieldname FROM mytable GROUP BY myfieldname
I'd at least like to do it the Django way before resorting to raw sql.
For example, with a table:
id, street, city
1, Main Street, Hull
2, Other Street, Hull
3, Bibble Way, Leicester
4, Another Way, Leicester
5, High Street, Londidium
I'd like to get:
Hull, Leicester, Londidium.
Say your model is 'Shop'
class Shop(models.Model):
street = models.CharField(max_length=150)
city = models.CharField(max_length=150)
# some of your models may have explicit ordering
class Meta:
ordering = ('city',)
Since you may have the Meta class ordering attribute set (which is tuple or a list), you can use order_by() without parameters to clear any ordering when using distinct(). See the documentation under order_by()
If you don’t want any ordering to be applied to a query, not even the default ordering, call order_by() with no parameters.
and distinct() in the note where it discusses issues with using distinct() with ordering.
To query your DB, you just have to call:
models.Shop.objects.order_by().values('city').distinct()
It returns a dictionary
or
models.Shop.objects.order_by().values_list('city').distinct()
This one returns a ValuesListQuerySet which you can cast to a list.
You can also add flat=True to values_list to flatten the results.
See also: Get distinct values of Queryset by field
In addition to the still very relevant answer of jujule, I find it quite important to also be aware of the implications of order_by() on distinct("field_name") queries. This is, however, a Postgres only feature!
If you are using Postgres and if you define a field name that the query should be distinct for, then order_by() needs to begin with the same field name (or field names) in the same sequence (there may be more fields afterward).
Note
When you specify field names, you must provide an order_by() in the
QuerySet, and the fields in order_by() must start with the fields in
distinct(), in the same order.
For example, SELECT DISTINCT ON (a) gives you the first row for each
value in column a. If you don’t specify an order, you’ll get some
arbitrary row.
If you want to e.g. extract a list of cities that you know shops in, the example of jujule would have to be adapted to this:
# returns an iterable Queryset of cities.
models.Shop.objects.order_by('city').values_list('city', flat=True).distinct('city')
By example:
# select distinct code from Platform where id in ( select platform__id from Build where product=p)
pl_ids = Build.objects.values('platform__id').filter(product=p)
platforms = Platform.objects.values_list('code', flat=True).filter(id__in=pl_ids).distinct('code')
platforms = list(platforms) if platforms else []
If you don't use PostgreSQL and you just want to get and distinct with only one specific field you can use
MyModel.objects.values('name').annotate(Count('id')).order_by()
this queryset return us rows that their 'name' field is unique with the count of the rows that have same name
<QuerySet [{'name': 'a', 'id__count': 2}, {'name': 'b', 'id__count': 2}, {'name': 'c', 'id__count': 3}>
I know the returned data is not complete and sometimes is not satisfying but sometimes it's useful
document reference
The SELECT DISTINCT statement is used to return only distinct (different) values. Inside a table, a column often contains many duplicate values; using distinct() we can get Unique data.
event = Event.objects.values('item_event_type').distinct()
serializer= ItemEventTypeSerializer(event,many=True)
return Response(serializer.data)