Skip to content

Querying for content

Simon Yohannes edited this page Nov 27, 2019 · 31 revisions

using the QueryHelper class

the QueryHelper<T> 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:

var qh = new QueryHelper<Page>();

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:

   var qh = new QueryHelper<Page>()
   .ExplicitType();

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:

   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:

   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:

qh.GreaterThanEqualTo(x => x.Age, 18);
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

public class Page:BaseModel
{
    [Display(ShortName ="input",GroupName ="Content")]
    [UIHint("ListEditor")]
    public List<string> Names { get; set; }
}

given the model above has a Names property which is a List<string>, you can search for any names in the list by doing the following:

    var qh = new QueryHelper<Page>();
    qh.Must().Field(x=>x.Names,"Joe")
    .GetAll()

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<Person> property, "People", and that Person class has a property Age. to search for people with an age of 21 you would do the following:

    var qh = new QueryHelper<Page>();
    qh.Must().Field(x=>x.People[0].Age,21)
    .GetAll()

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.

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.

public class Page:BaseModel
    {
        [UIHint("PuckGoogleLongLat")]
        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:

    var geoQuery = new QueryHelper<Page>();
    geoQuery.WithinMiles(x => x.Location.LatLong, 51.5073509, -0.1277582, 10);
    var georesults = geoQuery.GetAll();

the query above searches for Page ViewModels which are within 10 miles of Latitude 51.5073509, and Longitude -0.1277582. there is also a WithinKilometers method if you prefer.

if you don't want to use the GeoPosition class, you can specify your own Property as being a spatial field:

    [IndexSettings(Spatial=true)]
    public string LatLong { 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 "Latitude,Longitude". eg "51.50,-0.12" - notice the comma separating the Latitude from the Longitude and also notice it's Y,X or Latitude first.

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:

var descendants = Model.Descendants<Page>();

if you're using QueryHelper rather than the extension methods, you also have access to Ancestors, Siblings, Children and Descendants methods. for example:

    var qh = new QueryHelper<Section>()
        .Descendants(Model)
        .ExplicitType()
        .GetAll();

the query above will get the descendants of the current Model which are of type Section

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:

    var qh = new QueryHelper<Page>();
    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 PuckPicker

if you've got List<PuckPicker> properties in your ViewModels and have selected content/images which you want to retrieve, you can use the GetAll<T>() and Get<T>() 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 the current page and want to retrive its ViewModel from your controller action, you can do this:

var currentNode = QueryHelper<Page>.Current();

Highlighting text

you can use the string extension method Highlight in the puck.core.Extensions namespace to highlight a search term within a body of text. it will wrap the term within a <span> tag with the css class "search_highlight", which you can then style using css.

Clone this wiki locally