Not sure that I can answer your question but I think it's probably very relevant that you share your current code if it might need to be changed to speed things up? :)
Also it's probably nice to know the exact version of Umbraco 7 you're using.
If you can expand on your answer that would be great. Which API would you use for querying for multiple members based on a number of custom properties?
I would suggest you take a look at using Examine for searching through members.
Examine is build-in into Umbraco and uses Lucene.net under the hood.
I've found a few examples for you:
// Retrieve what to query for somehow
string query = (Request["q"] ?? "").Trim();
// Use the already configured member searcher
BaseSearchProvider memberSearcher = ExamineManager.Instance.SearchProviderCollection["InternalMemberSearcher"];
// Here we use Examines own query syntax, which is just a thin wrapper/api for generating Lucene queries
// Hopefully it's fairly self-explanatory
// A fair warning: you need to take a few extra steps to enable sorting on custom properties
ISearchCriteria allMembersCriteria = memberSearcher
.CreateSearchCriteria("member")
.NodeTypeAlias("SomeMemberTypeAlias")
.And()
.OrderBy("__nodeName")
.Compile();
// Execute the query and get back search results
ISearchResults resultsAllMembers = memberSearcher.Search(allMembersCriteria);
// Iterate through the results
// Fields is a dictionary where the key is the property type alias and the value is a string
foreach (var member in resultsAllMembers)
{
var name = member.Fields["name"];
var town = member.Fields.ContainsKey("town") ? member.Fields["town"] : "no town supplied";
}
// Here we supply a custom property to search in using wildcards
ISearchCriteria membersByTownCriteria = memberSearcher
.CreateSearchCriteria("member")
.NodeTypeAlias("SomeMemberTypeAlias")
.And()
.Field("town", query.MultipleCharacterWildcard())
.Compile();
ISearchResults resultsMembersByTown = memberSearcher.Search(membersByTownCriteria);
// In case your query gets too complex for Examines api to handle you can always write the lucene query yourself
ISearchCriteria membersByCustomCriteria = memberSearcher
.CreateSearchCriteria("member")
.RawQuery("Some native lucene query here");
ISearchResults resultsMembersByCustom = memberSearcher.Search(membersByCustomCriteria);
I first want to clarify the difference between APIs that are for front-end/back-office:
It's important to note that there's a difference between front-end and back office models/services. The MemberSerice is for querying members in the form of IMember, and for modifying them. IMember and the MemberService is for the back office/business logic/CRUD. These should not be exposed directly to the front-end views since you shouldn't expose business logic to the front-end as a general best practice. If you need to do this you'd convert the business logic models to your own view models. IPublishedContent and MembershipHelper is for the front-end views. However, the MembershipHelper doesn't currently expose any query methods ... which we should implement.
So back to performance, there's probably a reason why the GetMembersByPropertyValue is slow in your case and this would depend on how many members are returned from that query? I'm assuming it's quite a lot, and then you are filtering the result with an in-memory query. We could probably create an overload of that method to accept a collection of field queries which would certainly help with this.
Some feedback on your code as well:
var m = MemberService.GetMembersByPropertyValue(MemberStatusAlias, int.Parse(Status), ValuePropertyMatchType.LessThan).AsEnumerable();
The result from GetMembersByPropertyValue is already IEnumerable, you shouldn't also do AsEnumerable();
This seems like it might also slow thing down a bit:
m = m.Where(x => x.ContentTypeAlias.ToLower().Equals(MemberType.ToLower()).Where(x => s.Towns.Contains(x.GetValue("livesIn").ToString()))
First ,the case insensitive equality check should be:
that can return null and doing a ToString() will give you a null reference exception ('object reference not set...')
I'm also wondering with 's' is in the s.Towns, is that part of an enumeration as well? If so what enumeration is all this code inside?
Lastly, depending on how you are using your result m, you'd want to be sure you aren't doing stuff like
for(var i = 0;i<m.Count();i++)
m.ElementAt(i)....
Basically make sure you're not enumerating your result multiple times (i.e. Count() will enumerate the entire collection, then ElementAt will enumerate the entire collection to that point, etc...) These sorts of things can drastically affect performance.
So if you can't improve the performance of how you are using the query and it's simply the Umbraco query in the GetMembersByPropertyValue that is slow (most likely due to so many members being returned), then your options are limited until we add more support for member queries. If that is the case, I'd go with Mads' Examine solution.
I think that Umbraco.TypedMember also uses Examine under the hood like media does. It would be nice if you could use query methods with a simple API instead of doing all from the above examples.
Querying Members Is Slow
Hi,
I am currently querying for members with
MemberService.GetMembersByPropertyValue
but it is really quite slow.I have to query members on a few custom fields - does anyone know how I can do this in the most efficient manner.
Thanks in advance - regards L
Hi L
Not sure that I can answer your question but I think it's probably very relevant that you share your current code if it might need to be changed to speed things up? :)
Also it's probably nice to know the exact version of Umbraco 7 you're using.
/Jan
Hi Jan,
I have tried quite a few different ways but currently I am using:
Initial Call
Then in a filter method:
The only thing I havent tried is an SQL statement but I am not sure how to do this and then to cast to a List of IMember
Any help would be warmly appreciated.
Regards, L
Hello,
The member service is for editing members. Not for just querying it.
You need to use a different API for that. Just like you have the ContentService and IPublishedContent.
You can use @Umbraco.TypedMember or @Members.GetById for this: http://issues.umbraco.org/issue/U4-4379#comment=67-12760
Jeroen
Hiya Jeroen,
If you can expand on your answer that would be great. Which API would you use for querying for multiple members based on a number of custom properties?
Thanks and Regards L
Hi L
I would suggest you take a look at using Examine for searching through members.
Examine is build-in into Umbraco and uses Lucene.net under the hood.
I've found a few examples for you:
Hopefully this is enough to get you started.
If you haven't used Examine and/or Lucene before, I suggest you go read up on it a bit first.
There is a lot of great documentation and blog posts out there, but a great place to start is this friendly intro: http://24days.in/umbraco/2013/getting-started-with-examine/
Also, documentation can be found here: https://our.umbraco.org/documentation/reference/searching/examine/
Order on custom properties, search for 'EnableSorting' here: https://our.umbraco.org/documentation/Reference/Searching/Examine/full-configuration
I first want to clarify the difference between APIs that are for front-end/back-office:
It's important to note that there's a difference between front-end and back office models/services. The MemberSerice is for querying members in the form of IMember, and for modifying them. IMember and the MemberService is for the back office/business logic/CRUD. These should not be exposed directly to the front-end views since you shouldn't expose business logic to the front-end as a general best practice. If you need to do this you'd convert the business logic models to your own view models. IPublishedContent and MembershipHelper is for the front-end views. However, the MembershipHelper doesn't currently expose any query methods ... which we should implement.
So back to performance, there's probably a reason why the GetMembersByPropertyValue is slow in your case and this would depend on how many members are returned from that query? I'm assuming it's quite a lot, and then you are filtering the result with an in-memory query. We could probably create an overload of that method to accept a collection of field queries which would certainly help with this.
Some feedback on your code as well:
The result from GetMembersByPropertyValue is already IEnumerable, you shouldn't also do AsEnumerable();
This seems like it might also slow thing down a bit:
First ,the case insensitive equality check should be:
Also, you need a null check on the
that can return null and doing a ToString() will give you a null reference exception ('object reference not set...')
I'm also wondering with 's' is in the
s.Towns
, is that part of an enumeration as well? If so what enumeration is all this code inside?Lastly, depending on how you are using your result
m
, you'd want to be sure you aren't doing stuff likeBasically make sure you're not enumerating your result multiple times (i.e. Count() will enumerate the entire collection, then ElementAt will enumerate the entire collection to that point, etc...) These sorts of things can drastically affect performance.
So if you can't improve the performance of how you are using the query and it's simply the Umbraco query in the GetMembersByPropertyValue that is slow (most likely due to so many members being returned), then your options are limited until we add more support for member queries. If that is the case, I'd go with Mads' Examine solution.
I think that Umbraco.TypedMember also uses Examine under the hood like media does. It would be nice if you could use query methods with a simple API instead of doing all from the above examples.
Jeroen
is working on a reply...