I am working on with a firebase database and android studio.
I am making a recommendation system. The db has 26 travel destinations each with some attributes. (activities, region, weather, time of the year to go)
Destination number 1 : [ https://i.stack.imgur.com/hrkpP.png ]
Those are values that will never going to change.
On the other hand. i ask people a series of questions and they are being saved on the database. responses to questions 1,2, and 3: [ https://i.stack.imgur.com/HJgMn.png ]
I want to check on the responses if the response has anything in common with the destination features. and if it does, i want to increment an int called score ( i also created an arraylist Destination: is made with the name, the id from the destinations db and also has an int score)
Firebase lets me get an from a child that has many childs.
Iterable<DataSnapshot> destinosHijos = dataSnapshot.child("destinos").getChildren();
Iterable<DataSnapshot> destinosRes = dataSnapshot.child("respuestas").child(codigo).child(user).getChildren();
if i want to iterate two lists and compare something between them i would do something like this (hypotecally)
for(int i=0;i<destinationsDB.size();i++){
for(int j=0;j<responsesDB.size();j++){
String destinationR=destinationsDB.get(i).getRegion();
String responsesR=responsesDB.get(j).getRegion();
if(destinationR.equals(responsesR){
score++;
}
}
}
i can't do the same with de foreach. i tried to get all the destination's region and all the responses that had "region" in them. but when i use a foreach inside another is not the same with the forloop
if i try.
for (DataSnapshot hijo : destinosHijos) {
// all of the regions from all of the destinations
String destinoRegion= hijo.child("region").getValue().toString
}
for (DataSnapshot res : destinosRes) {
// all the responses with "region" on them
if (res.child("region").exists()) {
if(res.child("region").getValue.equals(destinoRegion)){
score++;
}
}
}
}
}
I modified it becuase the actual one is waay longer.
anyways. when i try to println - res.child("region").getValue.equals(region) - it only prints out 2 false times. that is 2 responses that have "region" on it.
instead of printint 2 * 26 = 72 times, that would be making comparing each response with each destination (that's what the forloop would do).
Thank you in advance if you read all that.
Related
I am creating an application that is some kind of a personal vocabulary. The database is of the following form.
Now I need to implement a pagination, partial retrieval of the words of a user, but preserving the lexicographical order. Keeping words as keys (/user/{uid}/words/{word}) is not suitable, because handling homographs will be impossible in the future (as their key will coincide). I decided to keep additional property word for each user, so that I can call db.getReference().child("users").child(uid).child("words").orderByChild("word").
This will retrieve all words of a user. Now I need to paginate this query, e.g. first download 20 words and then again 20 etc., but preserving lexicographical order.
{
"users" : {
"yXYSqB016JMr1FIc85pvMbvqDDt2" : {
"words" : {
"5v1a1PaDKnTvvOH19kaFTa1iyOx2" : {
"index" : 1,
"word" : "apple"
},
"kXHakBKxk9TrAlWL1vTOCe0akk80" : {
"index" : 2,
"word" : "house"
},
"xSKSqB312JMrsFig15pvMbvqAAt0" : { ... }
}
},
"zCAtMpl9uxSjG9dJarGktTTs20w2" : { ... }
},
"vocabulary" : {
"en" : {
"5v1a1PaDKnTvvOH19kaFTa1iyOx2" : {
"definitions" : {
"a fruit that grows on a tree" : true
},
"word" : "apple"
},
"kXHakBKxk9TrAlWL1vTOCe0akk80" : { ... },
"xSKSqB312JMrsFig15pvMbvqAAt0" : { ... }
}
}
}
You seem to come from a SQL way of thinking, where you paginate by specifying the number of items to get and the number of items to skip. This is index-based pagination.
Firebase on the other uses cursor-based pagination. You tell it the nimber of items to get and at which item to start (or end). You identify this item by the value of the property on which you order, in your case that is the value of word. Since the same value could potentially appear in multiple children, you can also specify the key (the thing starting with 5v1a1...) of the child at which to start/end as a second parameter.
So say that you have a page size of two. You get the first 2 words with:
DatabaseReference allWords = db.getReference().child("users").child(uid).child("words");
Query firstPage = allWords.orderByChild("word").limitToFirst(2);
When you attach a listener to this, you'll get the first two words. You'll need to remember the word and the key of the last word in this first page:
String lastWordOnPreviousPage = "house";
String lastKeyOnPreviousPage = "5v1a1...";
Now if you need the second page of two words, you get them by:
Query secondPage = allWords.orderByChild("word").startAt(lastWordOnPreviousPage, lastKeyOnPreviousPage).limitToFirst(2);
i am using firebase real-time database.
there is the user name like - user1 and then some supplies he requested like the supplies name - markers and then the quantity - like 5.
this is my JSON file
{
"Users" : {
"User1" : {
"Markers" : 5,
"Scissors" : 1,
"Staplers" : 4
},
"User2" : {
"Markers" : 2,
"Scissors" : 5,
"Staplers" : 3
}
}
}
i want to get back the supplies whan i ask for in the format of:
markers 5
scissors 1
Staplers 4
the information comes out via a listview but i don't know how to get via the listview both name and quantity, i can get only one of them.
The code i am using to get only the names is:
Set<String> set = new HashSet<String>();
Iterator i = dataSnapshot.getChildren().iterator();
while (i.hasNext()) {
set.add(((DataSnapshot) i.next()).getKey());
}
and the code i am using to get only the quantity is:
// two first lines are the same
while (i.hasNext()) {
set.add(((DataSnapshot) i.next()).getValue().toString());
}
Try something like this instead:
for (DataSnapshot supply : dataSnapshot.getChildren()) {
String key = supply.getKey();
String value = supply.getValue();
}
Also, you could have just saved i.next() to a variable, then you can access the key and value within the same loop without calling additional i.next() (which I presume is what the question is about).
i have changed the script to this one
key = (((DataSnapshot) i.next()).getKey());
value = ((dataSnapshot).child(key).getValue().toString());
set.add(key+" "+value);
it works very good, thanks any way.
I am new to firebase and Json so having some basic troubles with it. My Json file in Firebase is built like this:
{
"workout" : {
"Heavy Chest and Arms" : [ "Bench press", "Incline Dumbbell Press", "Cable Crossover", "Bicep Barbell Curls", "Alternate Dumbbell Curls", "Preachers Curls", "V-Bar Triceps Extensions", "Skull Crushers", "Sitting Calf raises" ],
"Light Back and Shoulders" : [ "Pull Ups", "Chins", "Cable Rows", "Lateral Pulldowns", "Dumbbell Rows", "Reverse Flies", "Arnold Press", "Side Raises", "Front Raises", "Rotary Cuffs" ]
}
}
First I want to get out a list with all the workouts, that list would contain Heavy Chest and Arms","Light Back and shoulders". After that I want to get out all the elements(child?) in those list, example "Bench press","Incline Dumbell press" etc. I tried several times for hours now experimenting with this:
myFirebaseRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
List<String> messages = dataSnapshot.getValue();
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
Here's a better structure to get you going
workouts
workout_id_0
category: "Heavy Chest and Arms"
elements
element_id_0: true
element_id_1: true
workout_id_1
category: "Light Back and Shoulders"
elements
element_id_0: true
elements
element_id_0
name: "Bench press"
description: "While laying flat on your back, arms at 90 degrees etc"
element_id_1
name: "Incline Dumbbell Press"
description: "Incline the backboard 30 degrees and etc etc"
You can do some snappy things with this structure:
Say you want to include a bench press in both the Heavy Chest and Light back workouts. This structure enables you to re-use your elements.
A year from now, you want to change the name of Bench press to Bench Press SUPER DUPER. You just change the name: within the element_id_0 and that falls into place
You can easily query for which workouts include element_id_1, the Incline dumbbell press
You can easily change the name of your workout. Instead of Light Back and Shoulders, it could be Light Lower Back and Shoulders with just changing one child node.
You should further consider how you want to use your data; do you need to find all elements that are just for the legs and create a workout based on those? Do you need to query or add additional data?
To print out all of the workout category names:
ref.on("child_added", function(snapshot, prevChildKey) {
var workout = snapshot.val();
console.log("Category: " + workout.category);
//the element references are here too! elements = workout.elements
});
It will take a little code to get the element names for each workout but this should get you going.
Firebase is a NoSQL database and as such should not be treated like a normalized SQL database.
To oversimplify you store data in the way you want to retrieve your data, even if that means saving the same data in separate places.
If you want to retrieve a list of workouts, have a workout names object that contains just the workout names (e.g. Heavy chest and arms, Light back and shoulders). Then if you want to retrieve the child elements, do so from another object that matches what you provided in the question.
Some good reading to cement this idea.
Good day all, I have a list of Objects (Let's call them ContactObject for simplicity). This object contains 2 Strings, Name and Email.
This list of objects will number somewhere around 2000 in size. The goal here is to filter that list as the user types letters and display it on the screen (IE in a recyclerview) if they match. Ideally, It would filter where the objects with a not-null name would be above an object with a null name.
As of right now, the steps I am taking are:
1) Create 2 lists to start and get the String the user is typing to compare to
List<ContactObject> nameContactList = new ArrayList<>();
List<ContactObject> emailContactList = new ArrayList<>();
String compareTo; //Passed in as an argument
2) Loop though the master list of ContactObjects via an enhanced for loop
3) Get the name and email Strings
String name = contactObject.getName();
String email = contactObject.getEmail();
4) If the name matches, add it to the list. Intentionally skip this loop if the name is not null and it gets added to the list to prevent doubling.
if(name != null){
if(name.toLowerCase().contains(compareTo)){
nameContactList.add(contactObject);
continue;
}
}
if(email != null){
if(email.toLowerCase().contains(compareTo)){
emailContactList.add(contactObject);
}
}
5) Outside of the for loop now as the object lists are build, use a comparator to sort the ones with names (I do not care about sorting the ones with emails at the moment)
Collections.sort(nameContactList, new Comparator<ContactObject>() {
public int compare(ContactObject v1, ContactObject v2) {
String fName1, fName2;
try {
fName1 = v1.getName();
fName2 = v2.getName();
return fName1.compareTo(fName2);
} catch (Exception e) {
return -1;
}
}
});
6) Loop through the built lists (one sorted) and then add them to the master list that will be used to set into the adapter for the recyclerview:
for(ContactObject contactObject: nameContactList){
masterList.add(contactObject);
}
for(ContactObject contactObject: emailContactList){
masterList.add(contactObject);
}
7) And then we are all done.
Herein lies the problem, this code works just fine, but it is quite slow. When I am filtering through the list of 2000 in size, it can take 1-3 seconds each time the user types a letter.
My goal here is to emulate apps that allow you to search the contact list of the phone, but seem to always to it quicker than I am able to replicate.
Does anyone have any recommendations as to how I can speed this process up at all?
Is there some hidden Android secret I don't know of that only allows you to query a small section of the contacts in quicker succession?
I am making an application using the ionic framework and I am using sqlite to store a list of about 150 rows. Each row has two attributes, ID and Name.
Now I am retrieving this data using a database factory which runs a query.
It works, however when I test it on an my Galaxy Tab 3 the list takes about 5-10 seconds to load and once it it loaded the list it super laggy scrolling through the list items.
Here's my controller
.controller('ActionSearchCtrl', function($scope, ActionSearchDataService, DBA, $cordovaSQLite){
var tablet = true;
var query = "select action FROM actions;";
$scope.items = [];
$scope.runSQL = function(){
DBA.query(query).then(function(result){
$scope.items = DBA.getAll(result);
});
};
if(tablet){$scope.runSQL()};
Here's my Database Factory
.factory('DBA', function($cordovaSQLite, $q, $ionicPlatform) {
var self = this;
// Handle query's and potential errors
self.query = function (query, parameters) {
parameters = parameters || [];
var q = $q.defer();
$ionicPlatform.ready(function () {
$cordovaSQLite.execute(herbsDatabase, query, parameters)
.then(function (result) {
q.resolve(result);
}, function (error) {
console.warn('I found an error');
console.warn(error);
alert(error.message);
q.reject(error);
});
});
return q.promise;
}
// Proces a result set
self.getAll = function(result) {
var output = [];
for (var i = 0; i < result.rows.length; i++) {
output.push(result.rows.item(i));
}
return output;
}
// Proces a single result
self.getById = function(result) {
var output = null;
output = angular.copy(result.rows.item(0));
return output;
}
return self;
})
So the query returns about 150 entries which I need all on one page (I've looked into infinite scrolling and pagination but my client wants all the items on one page so this is not an option. From what I've read, 150 entries shouldn't be too slow in terms of watchers as I am using ng-repeat for the list items to display. If anyone has a way I can display this many items using the cordovaSQLite plugin to make the list function quicker let me know! at the moment the list is pretty much unusable, I've tried it on other devices too and it has the same result.
I've also tried creating about 200 dummy objects in the controller (without making a call to the db to get data) and the performance is fine. That's why I am thinking it's an SQLite performance issue. This is my first post on here so apologies if I am not clear enough.
Okay, so I used the collection-repeat instead of ng-repeat in my template where the list was being generated. Dramatically increased the performance of the list. I hope this helps someone as it was driving me crazy!
Your problem may comes from several points.
Are you running on Android?
First, if you are running on android, be aware that list management and SCROLLING is pretty lame at present on ionic. On my case, only Native scroll was providing good results with keeping ng-repeat.
app.config(function($ionicConfigProvider) {
if(ionic.Platform.isAndroid())
$ionicConfigProvider.scrolling.jsScrolling(false);
});
Fasting up SQLite
I have clearly improved my performance of SQLite by defining correct index with this command :
'CREATE INDEX IF NOT EXISTS ' + _indexUniqueId + ' ON ' + _tableName + ' ' + IndexDefinition));
For your case, if your have 2 cols, (A and B) and if you are using only A to query on your table, then you need only one index on ColA, and above IndexDefinition will be equal to : (A)
Moreover, check that officiel sqlite doc to understand how index are managed :
https://www.sqlite.org/lang_createindex.html
Update to ionic RC5
If you have not done it yet, do it.
There huge improvements on scrolling ng-repeat on 1.0.0-rc5
Else
If those information does not work, please provide more info on your issue :
- is the list unscrollable ? or delays a lot before displaying ? or both ?