I have a node called quotes in Firebase. I'm facing issues while fetching data in Android for a particular range. I want to fetch 3 continues quotes id starting from 2. Here is my query database:
"quotes" : {
"-L75elQJaD3EYPsd4oWS" : {
"authorName" : "Hellen v",
"famousQuote" : "When one door of happiness closes, another opens; but often we look so long at the closed door that we do not see the one which has been opened for us.",
"id" : "1",
"uploadedBy" : "Admin"
},
"-L7GOvDNI-o_H8RvNwoN" : {
"authorName" : "Rocky Balboa",
"famousQuote" : "It's not about how hard you can hit; it's about how hard you can get hit and keep moving forward.",
"id" : "2",
"uploadedBy" : "Admin"
},
"-L7GP9oBv5NR1T6HlDd4" : {
"authorName" : "African proverb",
"famousQuote" : "If you want to go fast, go alone. If you want to go far, go together.",
"id" : "3",
"uploadedBy" : "Admin"
},
"-L7GPjM1F3_7Orcz0Q1q" : {
"authorName" : "A.P.J Abdul Kalam",
"famousQuote" : "Don’t take rest after your first victory because if you fail in second, more lips are waiting to say that your first victory was just luck.",
"id" : "4",
"uploadedBy" : "Admin"
},
Below is the rule which I'm using for quotes
"quotes": {
".indexOn": ".value"
}
How can I get quotes which has id 2,3 and 4?
If you have more than 4 records in your database, to solve this, you can use a query in which you should combine startAt() and endAt() methods to limit both ends of your query like this:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
Query query = rootRef.child("quotes").orderByChild("id").startAt("2").endAt("4");
query.addListenerForSingleValueEvent(/* ... */);
Please see here more informations about Firebase Query's startAt() method:
Create a query constrained to only return child nodes with a value greater than or equal to the given value, using the given orderBy directive or priority as default.
And here are more informations about Firebase Query's endAt() method:
Create a query constrained to only return child nodes with a value less than or equal to the given value, using the given orderBy directive or priority as default.
Edit: According to your comment, if you only want the items that have the id property set to 2, 3 and 4, you should use nested queries like this:
Query queryTwo = rootRef.child("quotes").orderByChild("id").equalsTo("2");
queryTwo.addListenerForSingleValueEvent(
List<Item> list = new ArrayList();
list.add(itemTwo);
Query queryThree = rootRef.child("quotes").orderByChild("id").equalsTo("3");
queryThree.addListenerForSingleValueEvent(
list.add(itemThree);
Query queryFour = rootRef.child("quotes").orderByChild("id").equalsTo("4");
queryFour.addListenerForSingleValueEvent(
list.add(itemFour);
//Do what you need to do with the list that contains three items
);
);
);
Related
I want to simply add one value to a tuple from MongoDB.
The key is query, and the variable position should be added as consumer-Variable in the tuple with the following code:
MongoCollection<Document> collection = ...
Bson filter = Filters.eq("query", queryName);
Bson update = Updates.addToSet("consumer", position);
collection.findOneAndUpdate(filter, update);
However, when I look into my database, it looks like a list "consumer" : [ NumberLong(88760) ] has been inserted and not a single value, as shown in the producer field:
{ "_id" : ObjectId(...), "query" : "1000", "consumer" : [ NumberLong(88760) ], "producer" : NumberLong(88760) }
I also tried Update.push() with the same result.
How can I add just a single value, without having it as list?
For example, if you need to update only 1 object from the list inside, you can do it in the format as:
.update_one({"_id": ObjectId(_id)}, {"$set": {"i.5": list[2]}})
where "i.5" - 5 index of the list i.
This question already has answers here:
Retrieve only the queried element in an object array in MongoDB collection
(18 answers)
Spring data Match and Filter Nested Array
(1 answer)
Closed 3 years ago.
This is a document I want to search. Collection is CustmObjects. This is one CustomObject
{
"id" : "id_1",
"name" : "name_1";
"additionalData" : [
{
"additionalDataId" : "id_1_1",
"additionalDataName" : "name_1_1",
"longText" : "A long story about..."
},
{
"additionalDataId" : "id_1_2",
"additionalDataName" : "name_1_2",
"longText" : "A longer story about danger..."
},
{
"additionalDataId" : "id_1_3",
"additionalDataName" : "name_1_3",
"longText" : "A longer story about danger and courage"
},
]
}
To retrieve a document with name name_1 is easy.
final nameToSearchBy = "name_1";
Query query = new Query();
Criteria criteria = Criteria.where("name").is(nameToSearchBy);
query.addCriteria(criteria);
mongoTemplate.findOne(query, CustomObject.class, "CUSTOM_OBJECTS_COLLECTION");
Now my question. How to retrieve additionalData with name "id_1_2"? I do not need to retrieve the whole document, only an entry in the array.
Of course, I can retrive a whole document and iterate through its additionalData values in java program. But I want to delegate this job to database server.
I tried
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(Criteria.where("name").is("name_1").and("additionalData.additionalDataName").is("name_1_2")),
Aggregation.project("additionalData"),
);
mongoTemplate.aggregate(aggregation, "CustmObjects", Object.class);
It returns all elements of the array. I need only one matching element.
Update
This is what I want to do
Retrieve only the queried element in an object array in MongoDB collection
db.find({shapes: {"$elemMatch": {name: "name_1_2"}}}, ... )
As I understand, what the second part is projection to select fields. I need all the fields from array element.
{"shapes.color": 1}
The second link
Spring data Match and Filter Nested Array
The result I want to get is this
{
"additionalDataId" : "id_1_2",
"additionalDataName" : "name_1_2",
"longText" : "A long story about..."
}
is more comlex, it has array inside of element which is inside of array.
Please, I need something more simple and working.
I have db of visits users to places, that contains place_id and user_id like this
{place_id : 1, user_id : 1}
{place_id : 1, user_id : 1}
{place_id : 1, user_id : 2}
{place_id : 2, user_id : 3}
{place_id : 2, user_id : 3}
And I want to get amount of distinct users in each place. I ended up with following native mongo aggregation:
db.collection.aggregate([{
$group: {
_id: "$place_id",
setOfUsers: {
$addToSet: "$user_id"
}
}
}, {
$project: {
distinctUserCount: {
$size: "$setOfUsers"
}
}
}])
And now I want to implement it using Spring Data, the problem now is $size operation in projection, since Spring data API does not have such, at least I haven't found it in reference.
GroupOperation group = Aggregation.group("place_id").addToSet("user_id").as("setOfUsers");
ProjectionOperation project = Aggregation.project(). .... ?
Maybe there is any way to create size field also, than the nested api can be used:
Aggregation.project().and("distinctUserCount").nested( ???);
Any help is appreciated.
I am going to answer this in "one hit", so rather than address your "$project" issue, I'm going to advise here that there is a better approach.
The $addToSet operator will create a "unique" array ( or "set" ) of the elements you ask to add to it. It is however basically another form of $group in itself, with the difference being the elements are added to an "array" ( or "set" ) in results.
This is "bad" for scalability, as your potential problem here is that the "set" actually exceeds the BSON limit for document size. Maybe it does not right now, but who knows what the code you write right now will be doing in ten years time.
Therefore, since $group is really the same thing, and you also need "two" pipeline stages to get the "distinct" count, then just to "two" $group stages instead:
Aggregation pipeline = newAggregation(
group(fields("place_id","user_id")),
group("_id.place_id").count().as("distinctUserCount")
);
Being the shell equivalent of:
[
{ "$group": {
"_id": { "place_id": "$place_id", "user_id": "$user_id" }
}},
{ "$group": {
"_id": "$_id.place_id",
"distinctUserCount": { "$sum": 1 }
}}
]
This is simple code and it is much more "scalable" as the individualt "user_id" values are at first contained in separate documents in the pipeline. Therefore the "second" $group ( in place of a $project with $size ) "counts" the distinct amounts that were already determined in the first grouping key.
Learn the limitations and pitfalls, and code well.
I have few json document with the following format :-
_source: {
userId: "A1A1",
customerId: "C1",
component: "comp_1",
timestamp: 1408986553,
}
I want to query the document based on the following :-
(( userId == currentUserId) OR ( customerId== currentCustomerId) OR (currentRole ==ADMIN) ) AND component= currentComponent)
I tried using the SearchSourceBuilder and QueryBuilders.matchQuery, but I wasnt able to put multiple sub queries with AND and OR operators.
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchQuery("userId",userId)).sort("timestamp", SortOrder.DESC).size(count);
How we query elasticsearch using OR and AND operators?
I think in this case the Bool query is the best shot.
Something like :
{
"bool" : {
"must" : { "term" : { "component" : "comp_1" } },
"should" : [
{ "term" : { "userId" : "A1A1" } },
{ "term" : { "customerId" : "C1" } },
{ "term" : { "currentRole" : "ADMIN" } }
],
"minimum_should_match" : 1
}
}
Which gives in Java:
QueryBuilder qb = QueryBuilders
.boolQuery()
.must(termQuery("component", currentComponent))
.should(termQuery("userId", currentUserId))
.should(termQuery("customerId", currentCustomerId))
.should(termQuery("currentRole", ADMIN))
.minimumNumberShouldMatch(1)
The must parts are ANDs, the should parts are more or less ORs, except that you can specify a minimum number of shoulds to match (using minimum_should_match), this minimum being 1 by default I think (but you could set it to 0, meaning that a document matching no should condition would be returned as well).
If you want to do more complex queries involving nested ANDs and ORs, simply nest other bool queries inside must or should parts.
Also, as you're looking for exact values (ids and so on), maybe you can use term queries instead of match queries, which spare you the analysis phase (if those fields are analyzed at all, which doesn't necessarily make sense for ids). If they are analyzed, you still can do that, but only if you know exactly how your terms are stored (standard analyzer stores them lower cased for instance).
If you use a query_string query, your ANDs and ORs will be interpreted as such by the Lucene library.
This allows you to search for
(currentUserId OR currentCustomerId) AND currentComponent
for instance. By default, the values will be searched for in all fields.
I want to find a document by some values in objects from one list and update that list object values.
While it finds the document by the list values and subtracts the value of topups.total.cnt and topups.total.revenue (if it didn't then they would be 3 and 36000000), it doesn't change the false to true except for the first object
in the list.
I think this line
"updateObj.append("$set", new BasicDBObject("topups.data.$.isRb", true))"
is the problem. It doesn't update the the object that the myQuery defines.
The below false should all be true after running the code three times.
"topups" : {
"data" : [
{
"val" : 12000000,
"chId" : 2,
"reqSys" : "222220002899",
"old" : NumberLong(189398),
"isRb" : true
},
{
"val" : 12000000,
"chId" : 2,
"reqSys" : "222220002899",
"old" : NumberLong(189398),
"isRb" : false
},
{
"val" : 12000000,
"chId" : 2,
"reqSys" : "222220002899",
"old" : NumberLong(189398),
"isRb" : false
}
],
"total" : {
"cnt" : 0,
"revenue" : 0
}
},
This is the code..
BasicDBObject myQuery = new BasicDBObject();
myQuery.append("d", new BasicDBObject("$gte", dayOfYear - 2).append("$lte", dayOfYear))
.append("m", msisdn)
.append("topups.data.reqSys", reqSystem)
.append("topups.data.isRb", false)
.append("topups.data.chId", 2)
.append("topups.data.old", preEventBalance)
.append("topups.data.val", callCost);
BasicDBObject updateObj = new BasicDBObject();
updateObj.append("$set", new BasicDBObject("topups.data.$.isRb", true))
.append("$inc", new BasicDBObject("topups.total.revenue", -callCost)
.append("topups.total.cnt", -1));
mongoDBcollection.update(myQuery, updateObj);
This looks like the same question as this: How to Update Multiple Array Elements in mongodb
The answer, sadly, being that you cannot, in a single operation, update all documents in an array.
I think you are looking for something like this,
http://docs.mongodb.org/manual/reference/method/db.collection.update/#update-a-document-element
The document (for 2.4 ) says that it will only update the first element of an array. So I guess can't be done using the $ operator.
You can try and do it either by updating individual element, like "topups.data.1.isRb" : true or Create an array outside with updated values and replace the entire array.
Hope this helps.
I don't want to update all the records at once (as the above ticket from dex mentions or the link for the question Trisha mentions). I want to update one at a time.
So, if the code above runs one time (and all "isRb" is false), all three list members match "myQuery" so I want one of three to be changed to "true" (not all three).
Second time the code runs, two list members match "myQuery" so I want one of two to be changed to "true" (not all two).
Third time the code runs, only one list member matches "myQuery" so I want that one to be changed to "true" (after all it is the only one left "false" after two runs).