I have a set of objects that I created according to some google docs recommendation for objects you want to have locality.
So I have objects with keys of the form 'Name#00000001'. My thing is I have an operation I need to get the last one of these so I can get them all with the code below.
var query = datastore.createQuery('Post')
.select('__key__')
.filter('__key__', '>=', datastore.key(['Post', req.query["Thread"] + "#00000001"]))
.filter('__key__', '<=', datastore.key(['Post', req.query["Thread"] + "#99999999"]));
Things is when I do stuff like
var query = datastore.createQuery('Post')
.select('__key__')
.filter('__key__', '>=', datastore.key(['Post', req.query["Thread"] + "#00000001"]))
.filter('__key__', '<=', datastore.key(['Post', req.query["Thread"] + "#99999999"]))
.order('__key__',{descending: true})
.limit(1);
It fails. It seems I can't order by key at all. The items have time stamps in addition to this, but it seems you can't filter by 1 item and order by another. Is there anyway to get the last item with a query without querying everything?
In Google Cloud-Java see StructuredQuery. You can .setOrderBy(OrderBy.asc("__key__"))
Related
So I came across this answer which allows limiting the collectionGroup query to a specific document: CollectionGroupQuery but limit search to subcollections under a particular document
However I also want to further filter results based on a specific field using where, which required an index. The query works without errors but it always returns empty snapshot:
const cityRef = firebase.firestore().doc('cities/cityId');
firebase.firestore().collectionGroup('attractions')
.where('name', '>=', keywords),
.where('name', '<=', keywords + '\uf8ff')
.orderBy('name')
.orderBy(firebase.firestore.FieldPath.documentId())
.startAt(cityRef.path),
.endAt(cityRef.path + "\uf8ff")
.get()
.then((querySnapshot) => {
console.log("Found " + querySnapshot.size + " docs");
querySnapshot.forEach((doc) => console.log("> " + doc.ref.path))
})
.catch((err) => {
console.error("Failed to execute query", err);
})
firebaser here
The problem is almost certainly that your query has range checks on two different fields (name and the document path), which isn't possible in Firestore's query model. As the documentation on query limitations says:
In a compound query, range (<, <=, >, >=) and not equals (!=, not-in) comparisons must all filter on the same field.
Your startAt and endAt clauses are just different ways of writing > and < as far as this limitation is concerned.
To understand why the SDK allow you to write this query, but doesn't give you the result you want, we'll have to dive a bit deeper into it, so... 👇
What is possible, is to pass all relevant fields to startAt and endAt so that it can determine the correct slice across all those field values.
Doing that would also remove the need to even have the where, so it'd be:
firebase.firestore().collectionGroup('attractions')
.orderBy('name')
.orderBy(firebase.firestore.FieldPath.documentId())
.startAt(keywords, cityRef.path),
.endAt(keywords + '\uf8ff', cityRef.path + "\uf8ff")
.get()
...
But this query now first looks for the documents starting at keywords and then if necessary for cityRef.path in there to disambiguate between multiple results.
What you want is the equivalent of this query:
const docId = firebase.firestore.FieldPath.documentId()l
firebase.firestore().collectionGroup('attractions')
.orderBy('name')
.where('name', '>=', keywords),
.where('name', '<=', keywords + '\uf8ff')
.orderBy(firebase.firestore.FieldPath.documentId())
.where(docId, '>=', cityRef.path),
.where(docId, '<=', cityRef.path + '\uf8ff')
Now it's immediately clear why this isn't possible, because we have range conditions on two fields.
I've been trying to get that working in this jsbin (https://jsbin.com/yiyifin/edit?js,console), so far without success, but I'll post back if I get it working or have a final verdict on why it doesn't work.
I'm finally learning better methods for my JS. I'm trying to find a way to go faster than I do so far :
In my code, I have two arrays :
one with unique keys in first position
one with those keys in first position but not unique. There are multiple entries with a certain value I want to filter.
The thing is I don't want to filter everything that is in the second array. I want to select some positions, like item[1]+item[5]+item[6]. What I do works, but I wonder if there isn't a faster way to do it ?
for (let i=0;i<firstArrayOfUniques.length;i++){
const findRef = secondArrayOfMultiple
.filter(item => item[0]==firstArrayOfUniques[i][0]);
// Afterwards, I redo a map and select only the second element,
//then I join the multiple answers
// Is there a way to do all that in the first filter?
const refSliced = findRef.map(x=>x[1]);
const refJoin = refSliced.join(" - ");
canevasSheet.getRange(1+i,1).setValue(refJoin);
}
The script snippet you quote will spend almost all of its running time calling the Range.setValue() method. It gets called separately for every data row. Use Range.setValues() instead, and call it just once, like this:
function moot(firstArrayOfUniques, secondArrayOfMultiple) {
const result = firstArrayOfUniques.map(uniqueRow =>
secondArrayOfMultiple
.filter(row => row[0] === uniqueRow[0])
.map(row => row[1])
.join(' - '));
canevasSheet.getRange(1, 1, result.length, result[0].length).setValues(result);
}
See Apps Script best practices.
I am trying to get the latest 3 data from the database and display them in reverse order in the HTML page.
Code:
var refForevent = database.ref('/events/post');
refForevent.orderByChild("intro").limitToLast(3).on('child_added', function(snapshot){
var eventlist = []
eventlist.push(snapshot)
console.log(eventlist)
eventlist.reverse()
document.getElementById("event1date").innerHTML = moment(eventlist[0].intro).format("MMM Do YYYY");
document.getElementById("event1title").innerHTML = eventlist[0].title;
document.getElementById("event2date").innerHTML = moment(eventlist[1].intro).format("MMM Do YYYY");
document.getElementById("event2title").innerHTML = eventlist[1].title;
document.getElementById("event3date").innerHTML = moment(eventlist[1].intro).format("MMM Do YYYY");
document.getElementById("event3title").innerHTML = eventlist[1].title;
})
Output: Output that I am getting
Database:
I see that the field intro contains a date.
Here is one solution:
Take the value of this field
Remove the hyphen separators (e.g. from 2020-12-10 you get the number 20201210)
Multiply this number by -1
Store the resulting value in another field
Sort on this field
Alternatively (and more elegant...), use the date to create a Timestamp, multiply it by -1 and use it as explained above, i.e. store the resulting value in another field and sort on this field.
Since you're listening to the child_added event, your function gets called with each post node that matches the query in the order in which Firebase returns them. So your eventlist will only ever contain one node at a time.
To reverse the items, you can either get Firebase to return the values in reverse order, as Renaud suggest (I'll also link some answers below), or you can listen to all results in once go and then reversing client-side (as your code already seem to be trying). The latter would look something like:
var refForevent = database.ref('/events/post');
refForevent.orderByChild("date").limitToLast(3).on('value', function(results){
var eventlist = []
results.forEach(function(snapshot) {
eventlist.push(snapshot.val())
});
eventlist.reverse()
console.log(eventlist);
...
})
So this:
Listens to the value event, instead of child_added, so that it gets all matching child nodes in one go.
If then loops over the results, adding them to the array,
It calls .val() on each child snapshot, to get the value from it.
For more on descending sorting, also see:
Display posts in descending posted order (the oldest I could find describing the trick with negative values)
firebase -> date order reverse (with many more links from there)
Sorting in descending order in Firebase database
If in sql, we can get it with:
select Max(Column_name) from Table_name
but still can't find simple way to get Max value of Column in Parse.com JS API.
Please explain me how to get Max Value of Column in JS API?
The best way to do this is to use Parse.Query (api) and order by descending, then obtain the first item in the result.
Edit:
Maybe, it's not a good idea to use order by in the situation that there are thousands(or even more) items as it's time consuming(complexity at least O(nlogn)). An alternative is to write a function of your own to choose the maximum value yourself with complexity of O(n).
Write a query for descending order and fetch the first object out of it. You will have the max value of the column there.
var query = new Parse.Query("something");
query.descending("column_name");
query.first({
success: function(result){
var max = result.get("column_name");
},
error: function(){
},
})
I am not sure if you can do it directly with a max function. You can create your own max function by doing a query to get all entries from that column and sorting them in descending order. There are APIs for this. Then choose the first value from the sorted list.
You can basically follow the same pattern from all other queries:
// Query for ID
ParseQuery<ParseObject> query = ParseQuery.getQuery("Table_Name");
// Condition
query.orderByDescending("ID");
// First object will be retrieved, this will be the max_value
query.getFirstInBackground(new GetCallback<ParseObject>() {
public void done(ParseObject object, ParseException e) {
if (object == null) {
Log.d("score", "The getFirst request failed.");
} else {
Log.d("score", "Retrieved the object.");
}
Now you can get the value using something like:
String mensagem = object.getString("ID");
Hopefully this will help you!
So I am using mongoose and node.js to access a mongodb database. I want to bump up each result based on a number (they are ordered by date created if none are bumped up). For example:
{ name: 'A',
bump: 0 },
{ name: 'B',
bump: 0 },
{ name: 'C',
bump: 2 },
{ name: 'D',
bump: 1 }
would be retreived in the order: C, A, D, B. How can this be accomplished (without iterating through every entry in the database)?
Try something like this. Store a counter tracking the total # of threads, let's call it thread_count, initially set to 0, so have a document somewhere that looks like {thread_count:0}.
Every time a new thread is created, first call findAndModify() using {$inc : {thread_count:1}} as the modifier - i.e., increment the counter by 1 and return its new value.
Then when you insert the new thread, use the new value for the counter as the value for a field in its document, let's call it post_order.
So each document you insert has a value 1 greater each time. For example, the first 3 documents you insert would look like this:
{name:'foo', post_order:1, created_at:... } // value of thread_count is at 1
{name:'bar', post_order:2, created_at:... } // value of thread_count is at 2
{name:'baz', post_order:3, created_at:... } // value of thread_count is at 3
etc.
So effectively, you can query and order by post_order as ASCENDING, and it will return them in the order of oldest to newest (or DESCENDING for newest to oldest).
Then to "bump" a thread in its sorting order when it gets upvoted, you can call update() on the document with {$inc:{post_order:1}}. This will advance it by 1 in the order of result sorting. If two threads have the same value for post_order, created_at will differentiate which one comes first. So you will sort by post_order, created_at.
You will want to have an index on post_order and created_at.
Let's guess your code is the variable response (which is an array), then I would do:
response.sort(function(obj1, obj2){
return obj2.bump - obj1.bump;
});
or if you want to also take in mind name order:
response.sort(function(obj1, obj2){
var diff = obj2.bump - obj1.bump;
var nameDiff = (obj2.name > obj1.name)?-1:((obj2.name < obj1.name)?1:0);
return (diff == 0) ? nameDiff : diff;
});
Not a pleasant answer, but the solution you request is unrealistic. Here's my suggestion:
Add an OrderPosition property to your object instead of Bump.
Think of "bumping" as an event. It is best represented as an event-handler function. When an item gets "bumped" by whatever trigger in your business logic, the collection of items needs to be adjusted.
var currentOrder = this.OrderPosition
this.OrderPosition = currentOrder - bump; // moves your object up the list
// write a foreach loop here, iterating every item AFTER the items unadjusted
// order, +1 to move them all down the list one notch.
This does require iterating through many items, and I know you are trying to prevent that, but I do not think there is any other way to safely ensure the integrity of your item ordering - especially when relative to other pulled collections that occur later down the road.
I don't think a purely query-based solution is possible with your document schema (I assume you have createdDate and bump fields). Instead, I suggest a single field called sortorder to keep track of your desired retrieval order:
sortorder is initially the creation timestamp. If there are no "bumps", sorting by this field gives the correct order.
If there is a "bump," the sortorder is invalidated. So simply correct the sortorder values: each time a "bump" occurs swap the sortorder fields of the bumped document and the document directly ahead of it. This literally "bumps" the document up in the sort order.
When querying, sort by sortorder.
You can remove fields bump and createdDate if they are not used elsewhere.
As an aside, most social sites don't directly manipulate a post's display position based on its number of votes (or "bumps"). Instead, the number of votes is used to calculate a score. Then the posts are sorted and displayed by this score. In your case, you should combine createdDate and bumps into a single score that can be sorted in a query.
This site (StackOverflow.com) had a related meta discussion about how to determine "hot" questions. I think there was even a competition to come up with a new formula. The meta question also shared the formulas used by two other popular social news sites: Y Combinator Hacker News and Reddit.