I am looking for a way to generate a unique ID for nosql database. Unlike relational database there is no idea of rows which means there is no last row to increment from.
The most common way to handle this is to use UUID's. But my problem is I need to add another ID (other than the UUID) which needs to be:
Unique
Unsigned Int32
Total data could reach around 50,000,000. So how would you generate somewhat unique uint32 ID's?
The UInt32 value type represents unsigned integers with values ranging from 0 to 4,294,967,295.
Only generated when a new user registers.
3 Id's are given to each new user.
Currently using Couchbase Server.
This problem has already been solved - I would suggest using the atomic Increment (or Decrement) functions in Couchbase - these are a common pattern to generate unique IDs.
Whenever the incr() method is called, it atomically increments the counter by the specified value, and returns the old value, therefore it's safe if two clients try to increment at the same time.
Pseudocode example (I'm no Node.JS expert!):
// Once, at the beginning of time we init the counter:
client.set("user::count", 0);
...
// Then, whenever a new user is needed:
nextID = client.incr("user::count", 1); // increments counter and returns 'old' value.
newKey = "user_" + nextID;
client.add(newKey, value);
See the Node.JS SDK for reference, and see Using Reference Doucments for Lookups section in the Couchbase Developer Guide for a complete usage example.
Here's a function that returns a unique identifier each time it's called. It should be fine as long as the number of items does not exceed the range of 32-bit integers, which seems to be the case given the described requirements. (warning: once the array of UIDs fills up, this enters an infinite loop. You may also want to create some sort of a reset function that can empty the array and thus reset the UID when necessary.)
var getUID = (function() {
var UIDs = [];
return function() {
var uid;
do {
uid = Math.random() * Math.pow(2, 32) | 0x0;
} while (UIDs[uid] !== undefined);
return UIDs[uid] = uid;
};
}());
if you will call this insert method by passing "user" as key then your docId will be auto increment as
user_0
user_1
user_2
etc...
Please note that couchbase will show one extra row in your bucket with key as meta id and next counter value as doc value. Do not get surprised if you use query like select count(*) total from table; as it will show one more than real count, to avoid use where clause so that this row won't be counted.
public insert(data: any, key: string) {
return new Promise((resolve, reject) => {
let bucket = CouchbaseConnectionManager.getBucket(`${process.env.COUCHBASE_BUCKET}`)
bucket.counter(key, 1, {initial:0}, (err:any, res:any)=>{
if(err){
this.responseHandler(err, res, reject, resolve);
}
const docId = key + "_" + res.value;
bucket.insert(docId, data, (error:any, result:any) => {
this.responseHandler(error, result, reject, resolve);
});
});
});
}
Related
Im just trying to insert a number of values into Firebase, for analysis reasons. In fact, im testing the database for a university project. The idea was to get the time it takes to insert and retrieve data.
The code im doing is simple, but it wont insert into the database the number of messages i want.
function sendMessage(times){
for(let i = 0; i < times; i++){
console.log(i); // this prints 50 times
let messageObj = {
user: "testUser" + i,
message: "message" + i
};
firebase.database().ref("History/" + Date.now()).set(messageObj); // this executes only a random number of times
}
window.onload = function () {
sendMessage(50);
}
Here is an image of the data inserted into Firebase Realtime Database.
As you can see, the data is being inserted in a random way. Why is this happening? Thanks in advance.
Using Date.now() for your node name is going to lead to multiple nodes ending up on the same millisecond. And since keys in Firebase are unique by definition, that means that you're overwriting the previous result on the same timestamp.
A slightly better approach is to also embed the value of i in the key, which ensures they are always unique:
firebase.database().ref("History/" + i + "_" + Date.now()).set(messageObj)
Even better would be to use Firebase's built-in push operations, which prevents this type of problem (and many others) entirely:
firebase.database().ref("History/").push(messageObj)
Im really confused on how to query a single record in firebase by the uid.
I have a user id and want to query the following table. I am using the following code.
However, it is returning both the records instead of just the 'this.selectedId' one. Is there 1) A way to just return the record being queried 2) keep the key instead of array of index 0, 1, 2, 3 etc...
Code used
const itemsRef = this.afs.collection<any>(`/profiles/`);
itemsRef.valueChanges(this.selectedId).subscribe(
x => {
console.log("Value change", x);
}
);
Image
Returned result
The valueChanges method doesn't take any parameters. If you want to monitor a single document
const itemRef = this.afs.doc<any>(`/profiles/`+this.selectedId);
itemRef.valueChanges()...
I'm developing a app and need get a random user from my firebase user list. Whenever a user registers, the system updates the user count on an especific node. So, I draw a number from 1 to the total user. And now, how do I select a user based on that number?
Assuming all of your users are stored in a /users node with keys of their uid and assuming the uid's are ordered (which they always are), there are several options.
1) Load all of the users from the /users node into an array and select the one you want via it's index. Suppose we want the 4th user:
let usersRef = self.ref.child("users")
usersRef.observeSingleEvent(of: .value, with: { snapshot in
let allUsersArray = snapshot.children.allObjects
let thisUserSnap = allUsersArray[3]
print(thisUserSnap)
})
While this works for a small amount of users, it could overwhelm the device if you have say, 10,000 users and lots of data stored in each node.
2) Create a separate node to just store the uid's. This is a significantly smaller dataset and would work the same way as 1)
uids
uid_0: true
uid_1: true
uid_2: true
uid_3: true
uid_4: true
uid_5: true
uid_6: true
uid_7: true
3) Reduce the size of your dataset further. Since you know how many users you have, split the dataset up into two sections and work with that.
using the same structure as 2)
let uidNode = self.ref.child("uids")
let index = 4 //the node we want
let totalNodeCount = 8 //the total amount of uid's
let mid = totalNodeCount / 2 //the middle node
if index <= mid { //if the node we want is in the first 1/2 of the list
print("search first section")
let q = uidNode.queryLimited(toFirst: UInt(index) )
q.observeSingleEvent(of: .value, with: { snapshot in
let array = snapshot.children.allObjects
print(array.last) //the object we want will be the last one loaded
})
} else {
print("search second section")
let q = uidNode.queryLimited(toLast: UInt(index) )
q.observeSingleEvent(of: .value, with: { snapshot in
let array = snapshot.children.allObjects
print(array.first) //the object we want will be the first one loaded
})
}
this method only returns 1/2 of the list so it's a much more manageable amount of data.
If you are talking about your authenticated users, the only way to retreive a list of them is by calling the corresponding admin function and applying your logic to it afterwards.
Another way could be writing a trigger for your authentication and store the userId with an incrementing number (and maybe save a totalUser field), then you only need to generate a random number and access said user.
I'm trying to search a Parse.com field which is an array for a partial string.
When the field is in String format I can do the following:
// Update the filtered array based on the search text and scope.
// Remove all objects from the filtered search array
[self.searchResults removeAllObjects];
// Filter the array using NSPredicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.busnumber contains[c] %#", searchText];
self.searchResults = [NSMutableArray arrayWithArray:[self.objects filteredArrayUsingPredicate:predicate]];
This works, however the new field I want to search in is an Array.
It works when I change the it to the following:
PFQuery * query = [PFQuery queryWithClassName:#"Bus"];
[query whereKey:#"route" equalTo:[NSString stringWithFormat:#"%#", searchText]];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
NSLog(#"Objects: %#", objects);
if (error)
{
NSLog(#"ERROR: %#", error.localizedDescription);
}
else
{
[self.searchResults removeAllObjects];
[self.searchResults addObjectsFromArray:objects];
[self.searchDisplayController.searchResultsTableView reloadData];
}}];
However I need the exact String for this.
I want to be able to search for parts of a string though, but when I change it to:
[query whereKey:#"route" containsString:[NSString stringWithFormat:#"%#", searchText]];
I get:
[Error]: $regex only works on string fields (Code: 102, Version: 1.7.4)
Any ideas? Thanks :)
What you've attempted is rational, but the string qualifiers on PFQuery work only on strings.
I've seen this theme frequently on SO: PFQuery provides only basic comparisons for simple attributes. To do anything more, one must query for a superset and do app level computation to reduce the superset to the desired set. Doing so is expensive for two reasons: app-level compute speed/space, and network transmission of the superset.
The first expense is mitigated and the second expense is eliminated by using a cloud function to do the app level reduction of the superset. Unless you need the superset records on the client anyway, consider moving this query to the cloud.
Specific to this question, here's what I think the cloud function would resemble:
// very handy to have underscore available
var _ = require('underscore');
// return Bus objects whose route array contains strings which contain
// the passed routeSubstring (request.params.routeSubstring)
Parse.Cloud.define("busWithRouteContaining", function(request, response) {
// for now, don't deal with counts > 1k
// there's a simple adjustment (using step recursively) to get > 1k results
var query = new Parse.Query("Bus");
query.find().then(function(buses) {
var matching = _.select(buses, function(bus) {
var route = bus.get("route");
var routeSubstring = request.params.routeSubstring;
return _.contains(route, function(routeString) {
return routeString.includes(routeSubstring);
});
});
response.success(matching);
}, function(error) {
response.error(error);
});
});
If you do decide to perform the reduction on the client and need help with the code, I can edit that in. It will be a pretty simple switch to predicateWithBlock: with a block that iterates the array attribute and checks rangeOfString: on each.
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!