def _build_intersecting_query( filter_group: sql.FilterGroup, acc_query: typing.Optional[QueryExpression], table: sql.Table, direction: str, ) -> QueryExpression: opposite_direction = "left" if direction == "right" else "right" document_set = build_document_set_intersection(table, filter_group) if acc_query is None: intersection = document_set else: intersection = q.intersection(acc_query, document_set) next_table = getattr(table, f"{direction}_join_table") if next_table is None or table.has_columns: return intersection next_join_key = getattr(table, f"{direction}_join_key") assert next_join_key is not None if next_join_key.name == "ref": next_foreign_key = getattr(next_table, f"{opposite_direction}_join_key") assert next_foreign_key is not None return _build_intersecting_query( filter_group, q.join( intersection, q.index( index_name( next_table.name, column_name=next_foreign_key.name, index_type=IndexType.REF, )), ), next_table, direction, ) return _build_intersecting_query( filter_group, q.join( intersection, q.index( index_name( table.name, index_type=IndexType.REF, foreign_key_name=next_join_key.name, )), ), next_table, direction, )
def _sort_document_set(document_set: QueryExpression, order_by: typing.Optional[sql.OrderBy]): if order_by is None: return q.paginate(document_set, size=common.MAX_PAGE_SIZE) if len(order_by.columns) > 1: raise exceptions.NotSupportedError( "Ordering by multiple columns is not yet supported.") ordered_column = order_by.columns[0] assert ordered_column.table_name is not None ordered_document_set = q.join( document_set, q.index( common.index_name( ordered_column.table_name, column_name=ordered_column.name, index_type=common.IndexType.SORT, )), ) if order_by.direction == sql.OrderDirection.DESC: ordered_document_set = q.reverse(ordered_document_set) return q.map_( q.lambda_(["_", "ref"], q.var("ref")), q.paginate(ordered_document_set, size=common.MAX_PAGE_SIZE), )
def test_join(self): join_refs = [self._create(n=12)["ref"], self._create(n=12)["ref"]] assoc_refs = [self._create(m=ref)["ref"] for ref in join_refs] source = query.match(self.n_index_ref, 12) self.assertEqual(self._set_to_list(source), join_refs) # For each obj with n=12, get the set of elements whose data.m refers to it. joined = query.join(source, lambda a: query.match(self.m_index_ref, a)) self.assertEqual(self._set_to_list(joined), assoc_refs)
def convert_to_ref_set(collection_name: str, index_match: QueryExpression) -> QueryExpression: """Convert value-based match set to set of refs. Params: ------- collection_name: Name of the source collection for the index. index_match: Match set of the index. Index must have values attribute of the form [{"field": ["data", <field>]}, {"field": ["ref"}]}] """ return q.join( index_match, q.lambda_( ["value", "ref"], q.match( q.index(index_name(collection_name, index_type=IndexType.REF)), q.var("ref"), ), ), )
def get_products(name=None, category=None, sort_by=None, size=5, after=None, before=None): sortDic = { "price_asc": q.index('products_sort_by_price_asc'), "price_desc": q.index('products_sort_by_price_desc'), "name_asc": q.index('products_sort_by_name_asc'), 'created_at_asc': q.index('products_sort_by_created_asc') } matches = [] if(name): matches.append(q.match(q.index('products_search_by_name'), name)) if(category): matches.append(q.match( q.index('products_search_by_category'), q.ref(q.collection('categories'), category) )) if(len(matches) == 0): matches.append(q.documents(q.collection('products'))) return fauna.query( q.map_( lambda _, ref: q.get(ref), q.paginate( q.join( q.intersection(matches), sortDic[sort_by or 'name_asc'] ), size=size, after=after, before=before ) ) )
def test_join(self): self.assertJson( query.join(query.match(query.index("widget")), query.index("things")), '{"join":{"match":{"index":"widget"}},"with":{"index":"things"}}')