Поиск Grails Lucene занимает много времени

Для фона я использую Grails v2.2.1 и плагин Searchable (v0.6.4) для моего приложения, хотя я новичок в настройке Lucene.

Журналы показывают, что поиск занимает 26 миллионов секунд, но транзакция компаса занимает около 15 секунд, чтобы вернуться:

2013-04-23 00:40:34,269 DEBUG grails.plugin.searchable.internal. compass.search.DefaultSearchMethod - query: [+kind:band +name:snoop], [4] hits, took [26] millis

2013-04-23 00:40:49,965 DEBUG org.compass.core.transaction.LocalTransaction - Committing local transaction on thread [http-bio-8080-exec-10] Compass [1176020804] Session [2089649487]

Похоже, что это больше проблема с Compass, чем с Lucene, так как запрос выполняется быстро, но отображение Compass привязывает мой Java-процесс к почти 100% CPU и зависает слишком долго.

У меня проиндексировано около 3500 доменных объектов, и моя модель домена выглядит следующим образом: я пытался индексировать только имена полей и идентификаторы, но, похоже, он отображает все в домене при просмотре через Люка.

package com.bandbot

class Band {
    def slugGeneratorService
    static searchable = {
        mapping {
            spellCheck "exclude"
            only: ['name', 'id']
        }
    }
    String name
    String biography
    String profileImage
    String slug
    String similarBands // this will store bands/url/pic in the form of Kanye West::url::img.png~Queen::url::img.png
    boolean onTour // is this band currently touring? (Info from lastfm)
    String mbid // This band's MusicBrainz ID see @ http://musicbrainz.org/doc/MusicBrainz_Identifier
    String bandUrl
    String lastFMUrl // stores the lastfm url
    Date dateOfInception
    Date dateDisbanded
    Date lastUpdated

    static belongsTo = [Genre, BandbotUser]

    static hasMany = [ events : Event, genres : Genre ]

    def beforeInsert() {
        lastUpdated = new Date()
        this.slug = slugGeneratorService.generateSlug(this.class, "slug", name)
    }

    def beforeUpdate() {
        lastUpdated = new Date()
        if (isDirty('name')) {
            this.slug = slugGeneratorService.generateSlug(this.class, "slug", name)
        }
    }

    static constraints = {
        name(nullable: false, blank: false, unique: true)
        slug(nullable: true)
        bandUrl(nullable: true)
        dateDisbanded(nullable: true)
        mbid(nullable: true)
        dateOfInception(nullable: true)
        biography(nullable: true)
        similarBands(nullable: true)
        lastUpdated(nullable: true)
        lastFMUrl(nullable: true)
        kind( display: false )
    }
    static mapping = {
        onTour defaultValue: false
        biography type: 'text'
        similarBands type: 'text'
    }

    String toString(){name}

}

Моя логика поиска в контроллере для групп:

def search() {
    if (!params.q?.trim()) {
        return [:]
    }
    try {
        def searchResult


        if (params.sort) {
            searchResult = searchableService.search(
                    params.q.trim(),
                    [offset: params.offset ? params.int('offset') : 0,
                            max: params.max ? params.int('max') : 10,
                    sort: params.sort, order: params.order? params.order : 'asc']
                    )
        }
        else {
            searchResult = searchableService.search(
                    params.q.trim(),
                    [offset: params.offset ? params.int('offset') : 0,
                            max: params.max ? params.int('max') : 10]
                    )
        }

        return [searchResult: searchResult, params: params]

    } catch (SearchEngineQueryParseException ex) {
        return [parseException: true, params: params]
    }

}

Любые идеи очень приветствуются. Это мой самообучающийся проект, и я действительно хочу найти правильный путь.:) Спасибо, Кевин

1 ответ

Решение

У меня возникла та же проблема с использованием плагина для поиска в недавнем приложении Grails, которое я разрабатывал. У меня было два предметных объекта с отношением один ко многим, которые я индексировал для поиска. Для простоты я просто показываю объекты Домена с их полями и связями. Я не показываю какую-либо карту или информацию об ограничениях. Вот мои оригинальные занятия

class CodeValue{
    static searchable ={
        only:['value', 'description']
        value boost: 2.0
    }
    String value
    String description
    static belongsTo = [codeset: CodeSet]
}
class CodeSet{
    static searchable ={
        only:['name', 'description']
        name boost: 2.0
    }

    String name
    String description
    static hasMany = [codeValues:CodeValue]
}

Поиск CodeValues ​​занимал> 17 секунд. Я проиндексировал более 1000 объектов CodeValue, но 17-секундное время поиска было неприемлемым. Я выяснил, что вызывает медленное время поиска, и это, похоже, связано с функциональностью Compass, встроенной в плагин Grails Searchable.

В рамках поиска все сопоставленные объекты маршалируются в индекс. Для набора объектов Домена в сотнях время выполнения этого маршалинга не так уж и плохо, однако, когда вы попадаете в 1000-е, это занимает значительное количество времени. Возможно, время также связано со сложностью объекта, который выстраивается? В любом случае, я нашел сообщение в блоге от парня, у которого была такая же проблема, как и у меня.

http://webcache.googleusercontent.com/search?q=cache:lebHKgX2yXUJ:blog.hououji.info/archives/165+&cd=10&hl=en&ct=clnk&gl=us

Подводя итог статьи, когда он искал более 1000 объектов, время поиска было больше 15 секунд. Так же, как то, что я испытывал.

Он упомянул две вещи:

1) Установите для параметра supportUnmarshall значение false, значение по умолчанию - true, в конфигурации "статический поиск" в доменном объекте. При установке этой опции результаты поиска не попадают в индекс, однако время поиска очень быстрое. Снижение значения этой опции до false означает, что результаты не будут содержать объекты, не маршалируемые из индекса, и вам придется извлечь соответствующий объект домена из базы данных, используя идентификатор, возвращаемый как часть результата поиска. Вы могли бы подумать, что это будет плохо, но это не так, и при использовании этого метода мои результаты поиска отображаются намного быстрее, чем раньше. Вот URL для информации о настройке опции supportUnmarshall http://grails.org/Searchable+Plugin+-+Mapping+-+Class+Mapping. Это последний вариант в разделе "Опции".

2) Включите "перезагрузку" в defaultMethodOptions конфигурационного файла Searchable.groovy. Поэтому поместите что-то вроде этого в файл Searchable.groovy:

defaultMethodOptions = [
    search: [reload: true, escape: false, offset: 0, max: 25, defaultOperator: "and"],
    suggestQuery: [userFriendly: true]
]

Вам нужно будет добавить плагин Searchable Config, чтобы обновить это значение. Подробную информацию о добавлении и редактировании файла конфигурации Searchable.groovy можно найти на веб-странице плагина Grail Searchable. Поскольку у меня нет достаточно высокой репутации. Я не могу опубликовать более двух ссылок, поэтому вам нужно перейти на веб-страницу для плагина Grails Searchable и найти документацию по установке плагина Searchable Config.

Чтобы привести пример улучшения производительности. Ранее поиск по CodeValues ​​занимал 17+ секунд. Теперь они выполнены за 0,002 секунды.

Окончательный код, который я написал, выглядел так:

class CodeValue{
    static searchable ={
        only:['value', 'description']
        value boost: 2.0
        supportUnmarshall false
    }
    String value
    String description
    static belongsTo = [codeset: CodeSet]
}
class CodeSet{
    static searchable ={
        only:['name', 'description']
        name boost: 2.0
        supportUnmarshall false
    }

    String name
    String description
    static hasMany = [codeValues:CodeValue]
}
Другие вопросы по тегам