8

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>
Cleanbeans
  • 655
  • 4
  • 12
  • 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. – leifcr Dec 08 '20 at 10:18

2 Answers2

11

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>
leifcr
  • 1,458
  • 11
  • 15
0

I think your expectations for startAt and endAt are not correct. I suggest reviewing the documentation.

About startAt, it says:

Use the startAt() or startAfter() methods to define the start point for a query. The startAt() method includes the start point, while the startAfter() method excludes it.

So, if you pass a snapshot for startAt() with limit 5, you are going to start the query at that document, and get 4 documents that follow it in the query order.

Similarly, use the endAt() or endBefore() methods to define an end point for your query results.

endAt() with a limit 5 causes the query results to stop at that document. So you will get that document, in addition to the 4 documents that precede it in the query order.

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
  • 1
    Thanks @Doug. I've reviewed the doc as suggested. My expectations are as you are describing. In simple terms, my hope was the result would be my focus doc ID: 15, Expect results to be: 11, 12, 13, 14, 15 ---- 15,16, 17,18,19. Actual results: 1, 2, 3, 4, 5 -- 15, 16, 17, 18, 19. I know you're a busy man any ideas/other suggestion would be gratefully received. – Cleanbeans Nov 03 '20 at 16:31
  • Without seeing the actual data and results, it's hard to know what's going wrong. Please edit the question with specific details that anyone can use to reproduce the behavior. – Doug Stevenson Nov 03 '20 at 16:53
  • Again thanks @Doug, I've simplified the above and copy code from my js file over. I've shown the data and the results I expected & get. Please advise on additional info you need. If you can direct me to a working example in a code pen or something that would be cool. Again thanks. – Cleanbeans Nov 03 '20 at 17:56