Запрос составного ключа в 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)
(наиболее общий к наименее универсальному атрибуту), и он должен работать нормально после того, как вы соответствующим образом переделаете свой запрос.
Вот ссылка из документа, объясняющая составные ключи и диапазоны с датами: Частичное выделение с составными ключами
У вас есть два варианта запроса документов по переменному количеству / порядку полей:
- Используйте многомерное представление (или пространственное представление), которое позволяет опускать части составного ключа в запросе. Вот пример использования такого представления: http://developer.couchbase.com/documentation/server/4.0/views/sv-example2.html
- Используйте N1QL, который позволяет вам динамически запрашивать любое количество полей. Убедитесь, что вы добавили индексы для полей, которые вы собираетесь запрашивать, и используйте оператор EXPLAIN, чтобы проверить, что ваши запросы выполняются так, как вы ожидаете. Вот как вы используете N1QL в Python: http://developer.couchbase.com/documentation/server/4.0/sdks/python-2.0/n1ql-queries.html
Как вы уже обнаружили, вы не можете использовать обычное представление, потому что вы можете запрашивать его только по точному порядку полей в вашем составном ключе.