Firestore query endBefore & endAt not working as expected
Asked Answered
D

2

10

I am using Node 10, firebase 8.0, firebase-admin 9.3.0. I've included a copy of my test code & client side js version.

Data

Document ID | name | is_visible
----------- | ---- | ----------
     1      |   a  | true
     2      |   b  | true
     3      |   c  | true
     4      |   d  | true
     5      |   e  | true
     6      |   f  | true
     7      |   g  | true
     8      |   h  | true
     9      |   i  | true

Code

var admin = require("firebase-admin");

const firebaseConfig = {xxx}

admin.initializeApp({
    credential: admin.credential.cert(firebaseConfig)
  });

const getDocs = async () =>{

  try {

    const cursor = await admin.firestore().collection('ordering').doc('5').get();

    const beforeDocs = await admin.firestore().collection('ordering')
    .orderBy("name", "desc")
    .endAt(cursor)
    .limit(2)
    .get()


      beforeDocs.forEach((doc)=> {
        console.log("Doc ID - ",doc.id, ", Name -", doc.get('name'));
      })

    
    const afterDocs = await admin.firestore().collection('ordering')
      .orderBy("name", "desc")
      .startAt(cursor)
      .limit(2)
      .get()


    afterDocs.forEach((doc)=> {
      console.log("Doc ID - ",doc.id, ", Name -", doc.get('name'));
    })

  }
  catch (error) {
    console.error(error);
  }
}

getDocs();

run using node getDocs.js

Expect Results

Doc ID -  6 , Name - f
Doc ID -  5 , Name - e
Doc ID -  5 , Name - e
Doc ID -  4 , Name - d

Actual results

Doc ID -  9 , Name - i
Doc ID -  8 , Name - h
Doc ID -  5 , Name - e
Doc ID -  4 , Name - d

My temporary work around for the endAt is to reverse the order to "asc" use startAt then within the QuerySnapshot reverse the docs array before the forEach.

I've created this JS to show my issue, this is running of my test collection, its super rough, please be kind, outputs to the console.

I'm clearly doing something wrong, but just not sure what.

<script src="https://www.gstatic.com/firebasejs/8.0.0/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.0.0/firebase-firestore.js"></script>

<body>
  <div id="output">Check the console!</div>
</body>
<script>

  var firebaseConfig = {
    apiKey: "AIzaSyAxLEo7LfABdATToJTMQGae3Vgum_CAlQg",
    authDomain: "bmtestbase.firebaseapp.com",
    databaseURL: "https://bmtestbase.firebaseio.com",
    projectId: "bmtestbase",
    storageBucket: "bmtestbase.appspot.com",
    messagingSenderId: "326028950684",
    appId: "1:326028950684:web:74b55887466c63b4a9c159"
  };

  // Initialize Firebase
  firebase.initializeApp(firebaseConfig);
  
const db = firebase.firestore()
   
// Setting the Focus point using a Snapshot 
db.collection('ordering').get()
  .then (docs => {
    docs.forEach(doc => {
          console.log("MYCODE_allDocs: DocID ", doc.id, ", Name ", doc.get('name'))
    })
  })

db.collection('ordering').doc('5').get()
  .then( focusSnapshot => {

    db.collection("ordering")
      .orderBy("name", "asc")
      .endAt(focusSnapshot)
      .limit(2)
      .get()
    .then(docs => {
  
       console.log("MYCODE_endAt_expected:","DocID 4, Name d")
       console.log("MYCODE_endAt_expected:","DocID 5, Name e")      

      docs.forEach(doc => {  
        console.log("MYCODE_endAt:","DocID ", doc.id, ", Name ", doc.get('name'))
      }); 
    })


    db.collection("ordering")
      .orderBy("name", "asc")
      .startAt(focusSnapshot)
      .limit(2)
      .get()
    .then(docs => {

       console.log("MYCODE_startAt_expected:","DocID 5, Name e")
       console.log("MYCODE_startAt_expected:","DocID 6, Name f")      

      docs.forEach(doc => {  
        console.log("MYCODE_startAt:","DocID ", doc.id, ", Name ", doc.get('name'))
      }); 
    })
  
})

</script>
Dehumanize answered 3/11, 2020 at 15:45 Comment(1)
I ended up ditching endAt/endBefore, as it does not work as expected. It seems like endBefore is not limiting from 'itself', but instead limiting from the queryCursor. Another solution is to store every startBefore pointer in an array and navigate back/forward in that. Not ideal, but it works.Herrenvolk
H
14

According to firebase support, you need to use limitToLast with endBefore and endAt

Rewriting your example like this should work:

<script src="https://www.gstatic.com/firebasejs/8.0.0/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.0.0/firebase-firestore.js"></script>

<body>
  <div id="output">Check the console!</div>
</body>
<script>

  var firebaseConfig = {
    apiKey: "AIzaSyAxLEo7LfABdATToJTMQGae3Vgum_CAlQg",
    authDomain: "bmtestbase.firebaseapp.com",
    databaseURL: "https://bmtestbase.firebaseio.com",
    projectId: "bmtestbase",
    storageBucket: "bmtestbase.appspot.com",
    messagingSenderId: "326028950684",
    appId: "1:326028950684:web:74b55887466c63b4a9c159"
  };

  // Initialize Firebase
  firebase.initializeApp(firebaseConfig);
  
const db = firebase.firestore()
   
// Setting the Focus point using a Snapshot 
db.collection('ordering').get()
  .then (docs => {
    docs.forEach(doc => {
          console.log("MYCODE_allDocs: DocID ", doc.id, ", Name ", doc.get('name'))
    })
  })

db.collection('ordering').doc('5').get()
  .then( focusSnapshot => {

    db.collection("ordering")
      .orderBy("name", "asc")
      .endAt(focusSnapshot)
      .limitToLast(2)
      .get()
    .then(docs => {
  
       console.log("MYCODE_endAt_expected:","DocID 4, Name d")
       console.log("MYCODE_endAt_expected:","DocID 5, Name e")      

      docs.forEach(doc => {  
        console.log("MYCODE_endAt:","DocID ", doc.id, ", Name ", doc.get('name'))
      }); 
    })


    db.collection("ordering")
      .orderBy("name", "asc")
      .startAt(focusSnapshot)
      .limit(2)
      .get()
    .then(docs => {

       console.log("MYCODE_startAt_expected:","DocID 5, Name e")
       console.log("MYCODE_startAt_expected:","DocID 6, Name f")      

      docs.forEach(doc => {  
        console.log("MYCODE_startAt:","DocID ", doc.id, ", Name ", doc.get('name'))
      }); 
    })
  
})

</script>
Herrenvolk answered 9/12, 2020 at 13:10 Comment(4)
Great work, please supply the url for the support I'd like to read through it.Dehumanize
Yeah this information is not explicit on Firebase DocumentationEley
It is incredible that this data is still not included in firebase documentation in almost 2024. Google? Really?Getty
For anyone using this, keep in mind that it will require an index in the reverse order as the limitToLast is simply flipping the query order as can see hereSprocket
F
0

Agree! You should just replace .limit() with .limitToLast() . After this .EndBefore() and .EndAt() methods will work.

Freakish answered 14/4, 2024 at 11:3 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.