Get document of a specific type
# help-with-umbraco
g
I created a document of a specific type that contains a configuration for some component. What is the best way to get this document out of the content tree? Checking the docs here: https://docs.umbraco.com/umbraco-cms/reference/querying left me confused a bit. For now I went with the following apporach:
var config = umbracoHelper.ContentAtRoot().DescendantsOrSelfOfType(MySpecialConfiguration.ModelTypeAlias).FirstOrDefault() as MySpecialConfiguration;
I know that there is only one config of this type so
FirstOrDefault()
is not problematic here. Is traversing the tree like that very consuming? Is it the correct way of getting the config ?
r
You're not too far off - but ContentAtRoot() returns a collection of IPublishedContent. Even if you only have 1 root node it will be a list of 1.
var rootNodes = umbracoHelper.ContentAtRoot();
EDIT - I just went and looked closer at DescendantsOrSelfOfType and you can indeed call it on a collection. I didn't think that was valid - thanks for showing me that 😀 depending on your configuration, and how much you mind (or don't mind) scoping in your query, you could start with a known node. So for example here is one of my site's root nodes. so when I call ContentAtRoot() I'm going to get the 5 root nodes (not recycle bin). But if I know I'm looking for maybe a Promo Code I might start with the settings node and traverse my way down from there
var storeSettings = umbracoHelper.ContentAtRoot().FirstOrDefault(x => x.ContentType.Alias == "storeProductSettings");
This eliminates the need to traverse all of the nodes, so faster, but might be a bit hardcoded for your taste. If you would rather look everywhere you could try something like this
Copy code
var specificNode = rootNodes
    .SelectMany(root => root.DescendantsOrSelf())
    .FirstOrDefault(node => node.ContentType.Alias == MySpecialConfiguration.ModelTypeAlias);
(.SelectMany(root => root.DescendantsOrSelf()) ensures that you traverse all descendants from each root node.) Let me know if this gets you going or if I can be of any further help -Roger https://cdn.discordapp.com/attachments/1338855214995738667/1338885595518337024/image.png?ex=67acb5e1&is=67ab6461&hm=e32eddbe957b13ebf02724098bbeb01aadd6ceb38cd1f5815c713b6b83bf66f0& https://cdn.discordapp.com/attachments/1338855214995738667/1338885595904086016/image.png?ex=67acb5e1&is=67ab6461&hm=4eebbda7ffebdd697379859888eadfbfbf266d30050e8fe3e3e7ce5430cd3040&
g
My configuration are all stored under Root (Home) so I think this
.SelectMany(root => root.DescendantsOrSelf())
is not really needed here. Right ? My question is if this approach is not too much time consuming and if I should use
IPublishedContentQuery
and after I get GUID and then I should get only that from UmbracoHelper. https://cdn.discordapp.com/attachments/1338855214995738667/1339179135053991946/image.png?ex=67adc742&is=67ac75c2&hm=c219fada7a9a1655a3296cbe0af25b8327c03a2a75a932557577b85c8f06108e&
m
examine vs linq.. always makes me think of v7 https://skrift.io/issues/testing-the-performance-of-querying-umbraco/ and v8 https://view.officeapps.live.com/op/view.aspx?src=https%3A%2F%2Fumbracospark.com%2Fmedia%2Fp0xgaq2u%2Fquerying-umbraco.pptx&wdOrigin=BROWSELINK and v10+ examine for a relatively simple targetted search might not always be quicker.. though can't find the newer benchmarks that I though Paul Seal had done in modern umbraco 🙂
r
yeah I think I kind of overcomplicated your first question (best way to get the document out of the content tree) and then didn't really answer the second (performance). 😕 It seems that DescendantsOrSelfOfType() is fine and performant for smaller trees and does not become problematic until many nodes. If you end up benchmarking the different calls I'd love to see the results. Fun fact - Having really just looked into DescendantsOrSelfOfType for the first time yesterday, I was curious, so I ended up cracking open the source code behind it. It looks like DescendantOrSelfOfType is just using a predicate filter on the content type and passing that to DescendantOrSelf() which is itself using SelectMany haha -Roger
j
I was profiling a site under load the other day and found that most of the CPU use was fetching a settings node with a call to
.Descendant<T>()
. Swapping to
.Children.FirstOrDefault(x=> x is MyContentType)
was 100x faster, and that was by no means a big site. Note that I didn't use
.FirstChild<T>()
which also iterates all the children before returning the first of those that has the type 😬
For this scenario, i.e. a simple predicate in a predictable place with a limited number of nodes to iterate (which config/settings nodes almost always are), navigation properties + a little bit of linq is always the fastest option. For more complex lookups, it depends so much on content structure and specifically child/sibling counts in the places that you're looking that there's no fair test to prove which approach is faster.
s
Descendant can be really heavy. Esp when there are a lot of nested nodes before the one you look for. Often when we know the path we use firstchild as that does not traverse the whole content tree but stays at its level. When it is more dynamic where the item is placed descendantorself is an option
g
Copy code
Stopwatch sw = Stopwatch.StartNew();

var config = umbracoHelper.ContentAtRoot().DescendantsOrSelfOfType(MySpecialConfiguration.ModelTypeAlias).FirstOrDefault() as MySpecialConfiguration;
Console.WriteLine($"content at root descendatas {sw.ElapsedMilliseconds} ms : {config?.Id}");
sw.Restart();

var config2 = umbracoHelper.ContentAtRoot().First()?.FirstChild<ConfigurationFolder>()?.Descendant<MySpecialConfiguration>();
Console.WriteLine($"content at first child descendatas {sw.ElapsedMilliseconds} ms: {config2?.Id}");
Copy code
content at root descendatas 7 ms : 7358
content at first child descendatas 7 ms: 7358

content at root descendatas 3 ms : 7358
content at first child descendatas 2 ms: 7358

content at root descendatas 6 ms : 7358
content at first child descendatas 9 ms: 7358
I did a little test with two approaches, but the results are not very telling 🙂
j
How about:
umbracoHelper.ContentAtRoot().DescendantsOrSelfOfType(MySpecialConfiguration.ModelTypeAlias).FirstOrDefault()
vs
Umbraco.ContentAtRoot().FirstOrDefault().FirstChild<ConfigurationFolder>().FirstChild<MySpecialConfiguration>();
Or even
Umbraco.ContentAtRoot().FirstOrDefault().Children.FirstOrDefault(x => x is ConfigurationFolder).Children.FirstOrDefault(x => x is MySpecialConfiguration);
Assuming MySpecialConfiguration lives directly underneath ConfigurationFolder that is. I went from something like the top, to the something like the bottom line and went from about 500μs to about 5μs per call (and our site called it a LOT).
s
I guess in your example where the descendants does not have to traverse any nodes before finding the configuration nodes the difference might be nil. When you move the configuration below products in the tree, with descendants will also look in all product nodes before going to the config node. With firstchild approach, it then should only look for products and then configuration.
(you never know what clients do)
m
(are we pining for xpath for when it was the best for these anywhere in large structures... 🫣 )
29 Views