Запрос составного ключа в couchbase 4.0

Я получил представление, как это:

function (doc, meta) {
  if(doc.type){
    var id = doc.id ? doc.id: "";
    var company = doc.company ? doc.company: "";
    var store = doc.store ? doc.store: "";

    emit([doc.type, id, company, store]);
  }
}

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

def find_by_type_pageing_by_id_company_store(self, format_function=None, page=None, rows=None, recent=None, type=None, id="", company="", store="", include_docs=True):

    if not type:
        logger.error("No Type Provided in find by type query")
        raise exceptions.InvalidQueryParams("No Type Provided in find by type query")

    view = VIEW_BY_TYPE_VIN_COMPANY_STORE

    cb = self.get_cb_bucket()

    query = Query()

    # 'recent' and 'rows' are equivalent and will be unified to 'limit' here
    if recent and rows:
        raise exceptions.InvalidQueryParams(detail="Query may not contain both 'recent' and 'rows'")
    limit = rows or recent

    if limit:
        try:
            rows_per_page = int(limit)
        except ValueError:
            raise exceptions.InvalidQueryParams(detail="Query params 'recent' and 'rows' have to be integers")

        if rows_per_page > settings.PAGINATION_MAX_ROWS_LIMIT:
            raise exceptions.InvalidQueryParams(detail="Query params 'recent' and 'rows' may not exceed %s. "
                                                "Use the additional param 'page=2', 'page=3', etc. to access "
                                                "more objects" % settings.PAGINATION_MAX_ROWS_LIMIT)
        try:
            page = 1 if page is None else int(page)
        except ValueError:
            raise exceptions.InvalidQueryParams(detail="Query param 'page' has to be an integer")

        skip = rows_per_page * (page - 1)

        query.limit = rows_per_page
        query.skip = skip

    query.mapkey_range = [
        [type, id, company, workshop],
        [type, id + query.STRING_RANGE_END, company + query.STRING_RANGE_END, store + query.STRING_RANGE_END]
    ]

    rows = cb.query(view['doc'], view['view'], include_docs=include_docs, query=query)

    if format_function is None:
        format_function = self.format_function_default

    return_array = format_function(rows)
    return return_array

Он работает безупречно, когда запрашивает только определенный тип или тип и диапазон идентификаторов.

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

Я попробовал:

query.mapkey_range = [
    ["Vehicle", "", "abc", ""]
    ["Vehicle", q.STRING_RANGE_END, "abc", q.STRING_RANGE_END]
]

Я знаю, как-то важен порядок значений в составном ключе, поэтому запрос диапазона идентификаторов, вероятно, успешен.

Но я не смог найти подробного объяснения, как важен порядок и как справиться с этим вариантом использования.

Любая идея или намек, как справиться с этим? Заранее спасибо.

2 ответа

Решение

С составными ключами, заказ в emit определяет внутреннюю "сортировку" индекса. При использовании запроса диапазона используется этот порядок.

В твоем случае:

  • Индекс содержит все транспортные средства
  • все транспортные средства затем сортируются по идентификатору
  • для каждого идентичного идентификатора, транспортные средства сортируются по компании
  • для каждого идентичного идентификатора и компании, Транспортные средства затем сортируются по магазину

Давайте возьмем пример из 4 транспортных средств. Вот как будет выглядеть индекс:

Vehicle,a,ACME,store100
Vehicle,c,Stackru,store1001
Vehicle,d,ACME,store100
Vehicle,e,Stackru,store999

Вот что происходит с запросом диапазона:

  • Движок вида находит первую строку>= для startKey из вашего диапазона
  • Затем он находит последний, который <= до endKey вашего диапазона
  • Возвращает каждую строку в массиве

Вы можете видеть, как в зависимости от идентификаторов это может привести к плохим результатам: [["Vehicle", "", "ACME", ""], ["Vehicle", RANGE_END, "ACME", RANGE_END]] вот что происходит:

  • строка 1 (a) определяется как самый низкий, соответствующий startKey
  • строка 4 (e) не соответствует endKey, потому что "Vehicle,e,Stackru,store999" больше, чем "Vehicle,RANGE_END,ACME,RANGE_END" из-за третьего компонента
  • строка 3 (d) верхняя граница: Vehicle <= Vehicle, d <= RANGE_END, ACME <= ACME, store100 <= RANGE_END
  • следовательно, строка 1-3 возвращается, включая строку 2 из "Stackru"

TL / DR: порядок в emit имеет значение, вы не можете делать запросы с редкими "джокерами" в левой части составного ключа.

Измените функцию карты на emit(doc.type, doc.company, doc.store, id) (наиболее общий к наименее универсальному атрибуту), и он должен работать нормально после того, как вы соответствующим образом переделаете свой запрос.

Вот ссылка из документа, объясняющая составные ключи и диапазоны с датами: Частичное выделение с составными ключами

У вас есть два варианта запроса документов по переменному количеству / порядку полей:

  1. Используйте многомерное представление (или пространственное представление), которое позволяет опускать части составного ключа в запросе. Вот пример использования такого представления: http://developer.couchbase.com/documentation/server/4.0/views/sv-example2.html
  2. Используйте N1QL, который позволяет вам динамически запрашивать любое количество полей. Убедитесь, что вы добавили индексы для полей, которые вы собираетесь запрашивать, и используйте оператор EXPLAIN, чтобы проверить, что ваши запросы выполняются так, как вы ожидаете. Вот как вы используете N1QL в Python: http://developer.couchbase.com/documentation/server/4.0/sdks/python-2.0/n1ql-queries.html

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

Другие вопросы по тегам