GORM createCriteria с отношением один ко многим

С тех пор, как пару часов я пытаюсь понять, как создать критерий с критерием, который должен быть в списке.

Если я уменьшу свой код, у меня будут следующие два класса домена:

Отель - базовый класс домена, который я хочу получить:

class Hotel {

    static hasMany = [rooms: Room, amenities: HotelAmenity]

}

HotelAmenity - В отеле есть список удобств.

class HotelAmenity {

    String name

}

# 1 Мой первый подход был примерно таким:

PagedResultList pgl = Hotel.createCriteria().list(max: limit, offset: offset) {
    // ..
    and {
        amenities {
            'in'("id",myLongIdsList)
    }
}

Это работает. Но в этом случае мне возвращают каждый отель, в котором есть хотя бы одна из указанных анемий. Но моя желанная цель - найти отели, которые имеют все указанные удобства.

# 2 Итак, я попытался следовать

// ..
amenities {
    condition.hotelAmenities.collect { it.toLong() }.each {
        and {
            eq('id', it)
        }
}

Но этот код выглядит так, как будто он портит набор результатов, потому что он всегда возвращает пустой список, если определено более одной функции. Между прочим, в этом сценарии с одной услугой все отели, которые содержит мой список результатов, не будут иметь никаких других удобств в своем списке удобств, кроме того, который я искал - даже при том, что многие из них назначены. Я совершенно не понимаю этого поведения.

# 3 Еще один подход, с которым я пытался поиграть listDistinct вместо list, Но я больше не пытался это делать, когда понял, что не получаю PagedResultList но список hotel domain objects вернулся. И поэтому у меня не будет totalCount параметр (который мне нужен для моей функциональности разбивки на страницы)

Итак, я застрял между написанием собственного запроса и / или решением этой проблемы с помощью подхода GORM. Может также случиться так, что я немного запутался в том, как именно это должно работать.

Для любого уведомления или решения я был бы очень рад.

Спасибо Кристофер

PS: я использую Grails версии 1.3.7

PPS: Если какая-либо информация отсутствует, пожалуйста, дайте мне знать.

РЕДАКТИРОВАТЬ

Это решение, с которым я пришел вместе:

Map sqlParams = [:]
String mySql = "SELECT SQL_CALC_FOUND_ROWS hotel.id FROM hotel WHERE "
if(hotelAmenities.size()>0) {
  String commaSeperatedAmenities = hotelAmenities.toString()
  // the amenity IDs as comma seperates list so we can use them in the `IN` statement (ie 1,3,6,7)
  commaSeperatedAmenities = commaSeperatedAmenities.substring(1,commaSeperatedAmenities.length()-1)
  mySql += '( SELECT COUNT(hotel_amenities_id) from hotel_hotel_amenity ' +
           'WHERE hotel_amenities_id = hotel.id AND hotel_amenity_id IN ' +
           '(' + comaAmentities + ') ) = ' + condition.hotelAmenities.size()
}

mySql += 'LIMIT :offset, :limit'
sqlParams.put("offset",offset)
sqlParams.put("limit",limit)

def mresult = sqlInstance.rows(mySql,sqlParams)
List<Hotel> hotels = Hotel.getAll(mresult.collect {it.id})
int calcFoundRows = sqlInstance.rows("SELECT FOUND_ROWS()").get(0).get("FOUND_ROWS()").toString().toInteger()

2 ответа

Построить критерии на лету:

def neededAmenities = ...

PagedResultList pgl = Hotel.createCriteria().list(max: limit, offset: offset) {
    // ..
    amenities {
        and {
            neededAmenities.each { needed ->
                idEq (needed.id)
            }
        }
    }
}

В чистом SQL это не намного проще:

where exists(amenities.id == XXX) and exists(amenities.id == YYY) and (...) ... 

Вы могли бы пойти, вероятно, более оптимально с intersect запрос с семантикой, как это:

where intersect(
    select id from amenities where ..., 
    (:neededAmenities)) 
= (:neededAmenities)

Некоторый образец здесь.

редактировать: я пытался построить критерии второго типа и потерпел неудачу - я не вижу способа построить having count состояние Во всяком случае, это будет медленнее, чем несколько in -с как это придется countAmenities для всех hotels,

ОБНОВЛЕНИЕ, это должно дать вам список отелей, которые имеют все удобства, попробуйте это:

def amenitySize = HotelAmenity.list()?.size()
def hotelList = Hotel.createCriteria().list{
    sizeEq("amenities", amenitySize)
}
Другие вопросы по тегам