Home
Github
Wiki
Videos
Contact
Sponsor
Querying for Content
# using the QueryHelper class the `QueryHelper
` class is in the `puck.core.Helpers` namespace and is used to query for content stored in Lucene. Puck stores your content revisions in SQL Server using entity framework but that's for the backoffice. your web pages/templates retrieve content from Lucene, which is where the last published revision is stored and can be queried from. the T parameter is the Type of ViewModel you're trying to retrieve. you'd think that this: ```cs var qh = new QueryHelper
(); ``` would return all ViewModels of type `Page` but actually it returns all ViewModels who have `Page` in their inheritance chain. so basically all ViewModels which can be cast successfully to `Page`. if you want to return only `Page` content specifically, you can do the following: ```cs var qh = new QueryHelper
() .ExplicitType(); ``` if you want to search for ViewModels that implement a certain interface, you can do this: ```cs var qh = new QueryHelper
() .Implements
(); var results = qh.GetAllNoCast() .Cast
(); foreach(var viewModel in results){ @viewModel.MainContent } ``` notice that the type parameter for `QueryHelper` is `BaseModel` but that you specify the desired interface in the `Implements` method. you can then `Cast` the results to your interface. there is also a non generic overload for the `Implements` method which accepts `params Type[]` allowing you to specify multiple interfaces, so you can do this: ```cs var qh = new QueryHelper
() .Implements(typeof(IMainContent),typeof(IGallery)); ``` or this ```cs var interfaces = new Type[]{typeof(IMainContent),typeof(IGallery)}; var qh = new QueryHelper
() .Implements(interfaces); ``` only ViewModels which implement all specified interfaces will be returned. since you're using a `QueryHelper
` instance, you'll only be able to use `BaseModel` properties in your expressions, to get around this, you can do this: ```cs var qh = new QueryHelper
() .Implements
() .Must().Field
(x=>x.MainContent,"london"); ``` in the example above, you use the generic version of the `Field` method and specify your interface as its generic `Type` argument, you'll then be able to use expressions with properties from that interface. if you wanted to limit the search to the current root (if you have a multi-site setup) and also get only content of the current language (taking into account localisation settings for the current url) you would do the following: ```cs qh.CurrentRoot(Model) .CurrentLanguage(); ``` notice how you can chain methods. the `Model` argument above is the Model property of your template. you can also search fields in a strongly typed way: ```cs qh.Must().Field(x => x.MainContent, "london") ``` you can also chain multiple fields with `Or()` and the search will use the Lucene analyzers specified (per field) in the ViewModel being searched for. there are also methods for range queries: ```cs qh.GreaterThanEqualTo(x => x.Age, 18); ``` ```cs qh.Range(x => x.Updated, new DateTime(2019, 7, 12, 11, 28, 20), DateTime.Now, true, true); ``` the two boolean arguments for `Range()` are inclusive start and inclusive end, respectively. # Searching Lists ```cs public class Page:BaseModel { [Display(ShortName ="input",GroupName ="Content")] [UIHint("ListEditor")] public List
Names { get; set; } } ``` given the model above has a `Names` property which is a `List
`, you can search for any names in the list by doing the following: ```cs var results = new QueryHelper
() .Must().Field(x=>x.Names,"Joe") .GetAllNoCast() ``` the above query will search for `Page` ViewModels with name "Joe" present in its `Names` list. you can also search in lists of complex types. Let's say the `Page` ViewModel now has a `List
` property, "People", and that `Person` class has a property `Age`. to search for people with an age of 21 you would do the following: ```cs var results = new QueryHelper
() .Must().Field(x=>x.People[0].Age,21) .GetAllNoCast() ``` to clarify, the square brackets in `x.People[0].Age` is used just to access the `Age` property, it doesn't mean only search in index 0 of the list. it will search the whole list. # Execute a query you can execute your query by using the `Get()` or `GetAll()` methods on the `QueryHelper` instance. However, i would recommend using `GetAllNoCast()` and `GetNoCast()` so you always get the original type returned. this is useful since by default, `QueryHelper
` is returning any ViewModels which can be cast to type `T`, unless you use the `ExplicitType()` method to specify that you want only type `T` returned. so if you have the ViewModel inheritance chain `NewsPage:Page:BaseModel`, searching using `QueryHelper
` without specifying `ExplicitType()` will also return `NewsPage` ViewModels and if you use `GetAllNoCast()` they will be returned as actual `NewsPage` types but if you just use `GetAll()` they will be returned as type `Page`. # Geo Queries Puck also supports Geo queries. the first thing you need to do is include a `GeoPosition` property in your ViewModel. `GeoPosition` can be found in the `puck.core.Models` namespace. ```cs public class Page:BaseModel { public GeoPosition Location { get; set; } } ``` you can see in the ViewModel above, the location property is of type `GeoPosition` and it uses a google maps editor template to set the Longitude and Latitude values. to search this field, you would do the following: ```cs var geoQuery = new QueryHelper
() .WithinMiles(x => x.Location.LongLat, -0.1277582, 51.5073509, 10) .SortByDistanceFromPoint(x=> x.Location.LongLat,-0.127782, 51.5073509); var georesults = geoQuery.GetAll(); ``` the query above searches for `Page` ViewModels which are within 10 miles of Longitude -0.1277582 and Latitude 51.5073509. there is also a `WithinKilometers` method if you prefer. it also specifies a sort to make sure results are returned in order of closest distance from a particular longitude and latitude and there is an optional parameter to reverse the sort. if you don't want to use the GeoPosition class, you can specify your own Property as being a spatial field: ```cs [IndexSettings(Spatial=true)] public string LongLat { get; set; } ``` use the `IndexSettings` attribute to mark the property as a spatial field and the value for a spatial field must be a string in the format of "Longitude,Latitude". eg "-0.12,51.50" - notice the comma separating the Longitude from the Latitude (x,y). # BaseModel extension methods your ViewModels will all have access to extensions methods to help you get **Parent**, **Ancestors**, **Children**, **Descendants**, **Siblings** and **Variants**. here's an example of getting Descendants of the current ViewModel: ```cs var descendants = Model.Descendants
(); ``` if you're using `QueryHelper` rather than the extension methods, you also have access to `AncestorsOf`, `SiblingsOf`, `ChildrenOf` and `DescendantsOf` methods. for example: ```cs var results = new QueryHelper
() .DescendantsOf(Model) .ExplicitType() .GetAll(); ``` the query above will get the descendants of the current `Model` which are of type `Section` # Sorting you can sort on one or more fields by doing the following: ```cs var searchQuery = new QueryHelper
() .Must() .Field(x => x.Title, "london") .Sort(x=> x.Title) .Sort(x=> x.SortOrder); var results = searchQuery.GetAllNoCast(); ``` in the above query you're searching the title field for the term "london" and then sorting by title then sort order. # Inner queries and Groups you can pass in an inner query to the `Group()`, `And()`, `Or()` and `Not()` methods for more advanced queries. here's an example: ```cs var qh = new QueryHelper
(); var innerQuery = qh.New(); qh.Must().Group(innerQuery.Field(x=>x.MainContent,"news").Field(x=>x.MainContent,"events")); qh.GetAll(); ``` in the above query, you use `New()` to get a new inner query and then have a query where `MainContent` must contain either "news" or "events". # Getting selected content from PuckReference if you've got `List
` properties in your ViewModels and have selected content/images which you want to retrieve, you can use the `GetAll
()` and `Get
()` methods to retrieve selected content. these are extensions methods so you will need to have a using statement for the namespace `puck.core.Helpers`. # Getting the current page from a controller if you've [intercepted](/wiki/intercepting-requests) the current page and want to retrive its ViewModel from your controller action, you can do this: ```cs var currentNode = QueryHelper
.Current(); ``` ## String extension methods you can modify your search terms by using the following string extension methods: ```cs var qh = new QueryHelper
() .Field(x=>x.Title,"london".Wrap()) .Field(x=>x.Title,"london".WildCardSingle()) .Field(x=>x.Title,"london".WildCardMulti()) .Field(x=>x.Title,"london".Fuzzy(fuzzyness:2)) .Field(x=>x.Title,"london".Boost(boost:2)) .Field(x=>x.Title,"london".Proximity(proximity:2)) .Field(x=>x.Title,"london".Escape()); ``` in the examples above, string literals are used but the same applies for string variables. if you're unfamiliar with these `Lucene` modifiers, try this [reference](https://lucene.apache.org/core/4_8_0/queryparser/org/apache/lucene/queryparser/classic/package-summary.html#package_description). # Highlighting text you can use the string extension method `Highlight` in the `puck.core.Extensions` namespace (in the `ExtensionsMisc` class) to highlight search terms within a body of text. it will wrap the terms within a `
` tag with the css class "search_highlight", which you can then style using css. here's an example usage: `@Html.Raw(Model.MainContent.Highlight("news"))`. this will output the `MainContent` field with all occurrences of "news" encapsulated in a span tag with the css class "search_highlight". ## graphql style querying for headless sites if you're working with Puck in a headless way and want graphql style querying, read the [headless](/wiki/headless#queryendpointforreturningmultipleresultsgraphqlstyle) page.