Sorting Firestore分页-是否有任何查询与firebase兼容';那是limitToLast吗?
有没有办法用firestore实现反向分页? 我正在努力用firestore实现分页,但firestore对它的查询有限。可以通过startAt和limit方法进行正向分页,这是可以的。但是反向分页并不容易,因为我们只有endBefore和endAt方法,我们如何从给定文档中获取最后n个元素?我知道实时数据库有方法limitToLast。对firestore有这样的疑问吗?(我还需要实现多重排序,因此使用“ASC”或“DESC”排序获取最后的文档将不起作用) 非常感谢你的帮助Sorting Firestore分页-是否有任何查询与firebase兼容';那是limitToLast吗?,sorting,firebase,pagination,google-cloud-firestore,Sorting,Firebase,Pagination,Google Cloud Firestore,有没有办法用firestore实现反向分页? 我正在努力用firestore实现分页,但firestore对它的查询有限。可以通过startAt和limit方法进行正向分页,这是可以的。但是反向分页并不容易,因为我们只有endBefore和endAt方法,我们如何从给定文档中获取最后n个元素?我知道实时数据库有方法limitToLast。对firestore有这样的疑问吗?(我还需要实现多重排序,因此使用“ASC”或“DESC”排序获取最后的文档将不起作用) 非常感谢你的帮助 谢谢 与Cloud
谢谢 与Cloud Firestore中Firebase实时数据库的
limitToLast(…)
操作等效的是,将数据降序(这在Firestore中是可能的),然后只limit(…)
。如果您在实现此功能时遇到问题,请更新您的问题以显示您所做的工作
我同意这是一个次优的反向分页API,因为您以相反的顺序接收项目。我遇到了同样的问题,不理解为什么使用
limit
和endAt
不能返回我想要的结果。我试图实现一个列表,在这个列表中,您可以在两个方向上分页,首先向前,然后向后回到列表的开头
为了纠正这种情况,我决定只缓存每页的
startAfter
DocumentSnapshot
,这样就可以双向移动,这样我就不必使用endAt
。只有当用户在第一页以外的页面上时,文档集合发生移动或更改,但返回到第一页时,文档集合将重置为集合的开头。是。基于弗兰克的回答
在您的查询中有类似的内容
if(this.next){
//如果下一步,orderBy字段降序,则在最后一个字段之后开始
q、 orderBy('field','desc');
q、 startAfter(此标记);
}else if(此上一个){
//如果是上一个,则orderBy字段升序,在第一个字段之后开始
q、 orderBy('field','asc');
q、 startAfter(此标记);
}否则{
//否则,只需正常显示第一页结果
q、 orderBy('field','desc');
}
q、 限制(此页面大小);
然后在得到查询时将其反转
this.tests集合
.valueChanges({idField:'id'})
.烟斗(
点击(结果=>{
如果(这个上一个){
//如果以前,需要反转结果。。。
结果:反向();
}
})
)
我只想分享我的Firestore分页代码我正在使用带有NextJS的react钩子
您需要有“useFirestoreQuery”钩子,可以在这里找到
这是我的设置
/* Context User */
const {user} = useUser()
/* States */
const [query, setQuery] = useState(null)
const [ref, setRef] = useState(null)
const [reverse, setReverse] = useState(false)
const [limit, setLimit] = useState(2)
const [lastID, setLastID] = useState(null)
const [firstID, setFirstID] = useState(null)
const [page, setPage] = useState(1)
/* Query Hook */
const fireCollection = useFirestoreQuery(query)
/* Set Ref, **When firebase initialized** */
useEffect(() => {
user?.uid &&
setRef(
firebase
.firestore()
.collection('products')
.where('type', '==', 'vaporizers')
)
}, [user])
/* Initial Query, **When ref set** */
useEffect(() => {
ref && setQuery(ref.orderBy('id', 'asc').limit(limit))
}, [ref])
/* Next Page */
const nextPage = useCallback(() => {
setPage((p) => parseInt(p) + 1)
setReverse(false)
setQuery(ref.orderBy('id', 'asc').startAfter(lastID).limit(limit))
}, [lastID, limit])
/* Prev Page */
const prevPage = useCallback(() => {
setPage((p) => parseInt(p) - 1)
setReverse(true)
setQuery(ref.orderBy('id', 'desc').startAfter(firstID).limit(limit))
}, [firstID, limit])
/* Product List */
const ProductList = ({fireCollection}) => {
const [products, setProducts] = useState([])
useEffect(() => {
let tempProducts = []
let tempIDs = []
const {data} = fireCollection
for (const key in data) {
const product = data[key]
tempIDs.push(product.id)
tempProducts.push(<ProductRow {...{product}} key={key} />)
}
if (reverse) {
tempProducts.reverse()
tempIDs.reverse()
}
setFirstID(tempIDs[0])
setLastID(tempIDs.pop())
setProducts(tempProducts)
}, [fireCollection])
return products
}
别忘了 确保您设置了所有这些的索引。这是我的样子。
更简单的回答:Firestore现在有.limitToLast(),它的工作原理与您想象的完全一样。在我自己的Firestore包装中使用(我想我需要尽快发布):
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
// *** Paginate API ***
export const PAGINATE_INIT = 0;
export const PAGINATE_PENDING = -1;
export const PAGINATE_UPDATED = 1;
export const PAGINATE_DEFAULT = 10;
export const PAGINATE_CHOICES = [10, 25, 50, 100, 250, 500];
/**
* @classdesc
* An object to allow for paginating a table read from Firestore. REQUIRES a sorting choice
* @property {Query} Query that forms basis for the table read
* @property {number} limit page size
* @property {QuerySnapshot} snapshot last successful snapshot/page fetched
* @property {enum} status status of pagination object
* @method PageForward pages the fetch forward
* @method PageBack pages the fetch backward
*/
export class PaginateFetch {
Query = null;
limit = PAGINATE_DEFAULT;
snapshot = null;
status = null; // -1 pending; 0 uninitialize; 1 updated;
/**
* ----------------------------------------------------------------------
* @constructs PaginateFetch constructs an object to paginate through large
* Firestore Tables
* @param {string} table a properly formatted string representing the requested collection
* - always an ODD number of elements
* @param {array} filterArray an (optional) 3xn array of filter(i.e. "where") conditions
* @param {array} sortArray a 2xn array of sort (i.e. "orderBy") conditions
* @param {ref} ref (optional) allows "table" parameter to reference a sub-collection
* of an existing document reference (I use a LOT of structered collections)
*
* The array is assumed to be sorted in the correct order -
* i.e. filterArray[0] is added first; filterArray[length-1] last
* returns data as an array of objects (not dissimilar to Redux State objects)
* with both the documentID and documentReference added as fields.
* @param {number} limit (optional)
* @returns {PaginateFetchObject}
**********************************************************************/
constructor(
table,
filterArray = null,
sortArray = null,
ref = null,
limit = PAGINATE_DEFAULT
) {
const db = ref ? ref : fdb;
this.limit = limit;
this.Query = sortQuery(
filterQuery(db.collection(table), filterArray),
sortArray
);
this.status = PAGINATE_INIT;
}
/**
* @method Page
* @returns Promise of a QuerySnapshot
*/
PageForward = () => {
const runQuery = this.snapshot
? this.Query.startAfter(_.last(this.snapshot.docs))
: this.Query;
this.status = PAGINATE_PENDING;
return runQuery
.limit(this.limit)
.get()
.then((QuerySnapshot) => {
this.status = PAGINATE_UPDATED;
//*IF* documents (i.e. haven't gone beyond start)
if (!QuerySnapshot.empty) {
//then update document set, and execute callback
//return Promise.resolve(QuerySnapshot);
this.snapshot = QuerySnapshot;
}
return this.snapshot.docs.map((doc) => {
return {
...doc.data(),
Id: doc.id,
ref: doc.ref
};
});
});
};
PageBack = () => {
const runQuery = this.snapshot
? this.Query.endBefore(this.snapshot.docs[0])
: this.Query;
this.status = PAGINATE_PENDING;
return runQuery
.limitToLast(this.limit)
.get()
.then((QuerySnapshot) => {
this.status = PAGINATE_UPDATED;
//*IF* documents (i.e. haven't gone back ebfore start)
if (!QuerySnapshot.empty) {
//then update document set, and execute callback
this.snapshot = QuerySnapshot;
}
return this.snapshot.docs.map((doc) => {
return {
...doc.data(),
Id: doc.id,
ref: doc.ref
};
});
});
};
}
/**
* ----------------------------------------------------------------------
* @function filterQuery
* builds and returns a query built from an array of filter (i.e. "where")
* consitions
* @param {Query} query collectionReference or Query to build filter upong
* @param {array} filterArray an (optional) 3xn array of filter(i.e. "where") conditions
* @returns Firestor Query object
*/
export const filterQuery = (query, filterArray = null) => {
return filterArray
? filterArray.reduce((accQuery, filter) => {
return accQuery.where(filter.fieldRef, filter.opStr, filter.value);
}, query)
: query;
};
/**
* ----------------------------------------------------------------------
* @function sortQuery
* builds and returns a query built from an array of filter (i.e. "where")
* consitions
* @param {Query} query collectionReference or Query to build filter upong
* @param {array} sortArray an (optional) 2xn array of sort (i.e. "orderBy") conditions
* @returns Firestor Query object
*/
export const sortQuery = (query, sortArray = null) => {
return sortArray
? sortArray.reduce((accQuery, sortEntry) => {
return accQuery.orderBy(sortEntry.fieldRef, sortEntry.dirStr || "asc");
//note "||" - if dirStr is not present(i.e. falsy) default to "asc"
}, query)
: query;
};
对于CollectionGroup查询,我也有相应的工具,每个工具都有侦听器。谢谢您的回复。问题是我需要实现排序和分页,比如afs.collection('data',ref=>ref.orderBy('field1','asc')。orderBy('field2','desc')…)。有没有办法将这些排序结果保留在分页中?我颠倒了这个查询顺序(field1=>'desc',field2=>'asc',…),但不起作用。谢谢。最后我决定使用Algolia,现在它工作得很好。唯一的问题是,如果你在17页之后开始,你必须缓存17个文档,例如……你有更好的解决方法吗?在我自己的用例中,为每个页面缓存一个文档的要求是一个有效的折衷办法阅读下面的注释,或者在云函数中使用offset()。
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
// *** Paginate API ***
export const PAGINATE_INIT = 0;
export const PAGINATE_PENDING = -1;
export const PAGINATE_UPDATED = 1;
export const PAGINATE_DEFAULT = 10;
export const PAGINATE_CHOICES = [10, 25, 50, 100, 250, 500];
/**
* @classdesc
* An object to allow for paginating a table read from Firestore. REQUIRES a sorting choice
* @property {Query} Query that forms basis for the table read
* @property {number} limit page size
* @property {QuerySnapshot} snapshot last successful snapshot/page fetched
* @property {enum} status status of pagination object
* @method PageForward pages the fetch forward
* @method PageBack pages the fetch backward
*/
export class PaginateFetch {
Query = null;
limit = PAGINATE_DEFAULT;
snapshot = null;
status = null; // -1 pending; 0 uninitialize; 1 updated;
/**
* ----------------------------------------------------------------------
* @constructs PaginateFetch constructs an object to paginate through large
* Firestore Tables
* @param {string} table a properly formatted string representing the requested collection
* - always an ODD number of elements
* @param {array} filterArray an (optional) 3xn array of filter(i.e. "where") conditions
* @param {array} sortArray a 2xn array of sort (i.e. "orderBy") conditions
* @param {ref} ref (optional) allows "table" parameter to reference a sub-collection
* of an existing document reference (I use a LOT of structered collections)
*
* The array is assumed to be sorted in the correct order -
* i.e. filterArray[0] is added first; filterArray[length-1] last
* returns data as an array of objects (not dissimilar to Redux State objects)
* with both the documentID and documentReference added as fields.
* @param {number} limit (optional)
* @returns {PaginateFetchObject}
**********************************************************************/
constructor(
table,
filterArray = null,
sortArray = null,
ref = null,
limit = PAGINATE_DEFAULT
) {
const db = ref ? ref : fdb;
this.limit = limit;
this.Query = sortQuery(
filterQuery(db.collection(table), filterArray),
sortArray
);
this.status = PAGINATE_INIT;
}
/**
* @method Page
* @returns Promise of a QuerySnapshot
*/
PageForward = () => {
const runQuery = this.snapshot
? this.Query.startAfter(_.last(this.snapshot.docs))
: this.Query;
this.status = PAGINATE_PENDING;
return runQuery
.limit(this.limit)
.get()
.then((QuerySnapshot) => {
this.status = PAGINATE_UPDATED;
//*IF* documents (i.e. haven't gone beyond start)
if (!QuerySnapshot.empty) {
//then update document set, and execute callback
//return Promise.resolve(QuerySnapshot);
this.snapshot = QuerySnapshot;
}
return this.snapshot.docs.map((doc) => {
return {
...doc.data(),
Id: doc.id,
ref: doc.ref
};
});
});
};
PageBack = () => {
const runQuery = this.snapshot
? this.Query.endBefore(this.snapshot.docs[0])
: this.Query;
this.status = PAGINATE_PENDING;
return runQuery
.limitToLast(this.limit)
.get()
.then((QuerySnapshot) => {
this.status = PAGINATE_UPDATED;
//*IF* documents (i.e. haven't gone back ebfore start)
if (!QuerySnapshot.empty) {
//then update document set, and execute callback
this.snapshot = QuerySnapshot;
}
return this.snapshot.docs.map((doc) => {
return {
...doc.data(),
Id: doc.id,
ref: doc.ref
};
});
});
};
}
/**
* ----------------------------------------------------------------------
* @function filterQuery
* builds and returns a query built from an array of filter (i.e. "where")
* consitions
* @param {Query} query collectionReference or Query to build filter upong
* @param {array} filterArray an (optional) 3xn array of filter(i.e. "where") conditions
* @returns Firestor Query object
*/
export const filterQuery = (query, filterArray = null) => {
return filterArray
? filterArray.reduce((accQuery, filter) => {
return accQuery.where(filter.fieldRef, filter.opStr, filter.value);
}, query)
: query;
};
/**
* ----------------------------------------------------------------------
* @function sortQuery
* builds and returns a query built from an array of filter (i.e. "where")
* consitions
* @param {Query} query collectionReference or Query to build filter upong
* @param {array} sortArray an (optional) 2xn array of sort (i.e. "orderBy") conditions
* @returns Firestor Query object
*/
export const sortQuery = (query, sortArray = null) => {
return sortArray
? sortArray.reduce((accQuery, sortEntry) => {
return accQuery.orderBy(sortEntry.fieldRef, sortEntry.dirStr || "asc");
//note "||" - if dirStr is not present(i.e. falsy) default to "asc"
}, query)
: query;
};