views:

438

answers:

4

Hi,

I'm trying to use Lucene to query a domain that has the following structure

Student 1-------* Attendance *---------1 Course

The data in the domain is summarised below

Course.name   Attendance.mandatory   Student.name
-------------------------------------------------
cooking       N                   Bob
art           Y                   Bob

If I execute the query "courseName:cooking AND mandatory:Y" it returns Bob, because Bob is attending the cooking course, and Bob is also attending a mandatory course. However, what I really want to query for is "students attending a mandatory cooking course", which in this case would return nobody.

Is it possible to formulate this as a Lucene query? I'm actually using Compass, rather than Lucene directly, so I can use either CompassQueryBuilder or Lucene's query language.

For the sake of completeness, the domain classes themselves are shown below. These classes are Grails domain classes, but I'm using the standard Compass annotations and Lucene query syntax.

@Searchable
class Student {

    @SearchableProperty(accessor = 'property')
    String name

    static hasMany = [attendances: Attendance]

    @SearchableId(accessor = 'property')
    Long id

    @SearchableComponent
    Set<Attendance> getAttendances() {
        return attendances
    }
}

@Searchable(root = false)
class Attendance {

    static belongsTo = [student: Student, course: Course]

    @SearchableProperty(accessor = 'property')
    String mandatory = "Y"

    @SearchableId(accessor = 'property')
    Long id

    @SearchableComponent
    Course getCourse() {
        return course
    }
}

@Searchable(root = false)
class Course {

    @SearchableProperty(accessor = 'property', name = "courseName")
    String name  

    @SearchableId(accessor = 'property')
    Long id
}
A: 

Try

+courseName:cooking +mandatory:Y

We use pretty similar queries and this works for us:

+ProdLineNum:1920b +HouseBrand:1

This selects everything in product line 1920b that is also a house brand (generic).

Bob King
Thanks for the suggestion, but it doesn't work - I get the same result as when I omit the '+' signs
Don
A: 

You can just create queries as text string and then parse that to get your query object. Presume you have seen Apache Lucene - Query Parser Syntax ?

Dan Diplo
+2  A: 

What you are trying to do is sometimes known as "scoped search" or "xml search" - the ability to search based on a set of related sub-elements. Lucene does not support this natively but there are some tricks you can do to get it to work.

You can put all of the course data associated with a student in a single field. Then bump the term position by a fixed amount (like 100) between the terms for each course. You can then do a proximity search with phrase queries or span queries to force a match for attributes of a single course. This is how Solr supports multi-valued fields.

KenE
+1  A: 

Another workaround is to add fake getter and index it

Something like:

@SearchableComponent
Course getCourseMandatory() {
    return course + mandatory;
}
coldsrnt