This question already has answers here:
Update MongoDB field using value of another field
(12 answers)
Closed 6 years ago.
I am trying to do a query that seems to be basic on the surface. However, I cannot seem to do it efficiently and elegantly. Namely, I have the following document:
{
id:
runtime: {
start: Date
end: Date
total: number
}
events: [runtime1, runtime2, ...]
}
Upon a certain API call, I would like to set the runtime.end to the current date and then push what is contained in the runtime object into the events array.
Here is the code I have been trying to use for it:
router.get('/stop/:id', function(req,res){
var collection = db.get('Activity');
collection.update({
_id: req.params.id
},
{
$set: {
"runtime.started": false,
"runtime.endDate": new Date()
},
$push: {events: {
startDate: new Date,
endDate: new Date
}
}
},
function(err, activity){
if (err) throw err;
res.json(activity);
});
});
Any idea how this can be done? The code above seem to do nothing, except to edit the runtime object.
P.S. I found several questions and answers circa 2010 - 2012 that stated that this could not be done. Have things changed since then?
Check your MongoDB version. You may need to upgrade to the newest one. Also, try with the $push by itself (without the set, just as in the MongoDB docs example).
Related
{
"movies": {
"movie1": {
"genre": "comedy",
"name": "As good as it gets",
"lead": "Jack Nicholson"
},
"movie2": {
"genre": "Horror",
"name": "The Shining",
"lead": "Jack Nicholson"
},
"movie3": {
"genre": "comedy",
"name": "The Mask",
"lead": "Jim Carrey"
}
}
}
I am a Firebase newbie. How can I retrieve a result from the data above where genre = 'comedy' AND lead = 'Jack Nicholson'?
What options do I have?
Using Firebase's Query API, you might be tempted to try this:
// !!! THIS WILL NOT WORK !!!
ref
.orderBy('genre')
.startAt('comedy').endAt('comedy')
.orderBy('lead') // !!! THIS LINE WILL RAISE AN ERROR !!!
.startAt('Jack Nicholson').endAt('Jack Nicholson')
.on('value', function(snapshot) {
console.log(snapshot.val());
});
But as #RobDiMarco from Firebase says in the comments:
multiple orderBy() calls will throw an error
So my code above will not work.
I know of three approaches that will work.
1. filter most on the server, do the rest on the client
What you can do is execute one orderBy().startAt()./endAt() on the server, pull down the remaining data and filter that in JavaScript code on your client.
ref
.orderBy('genre')
.equalTo('comedy')
.on('child_added', function(snapshot) {
var movie = snapshot.val();
if (movie.lead == 'Jack Nicholson') {
console.log(movie);
}
});
2. add a property that combines the values that you want to filter on
If that isn't good enough, you should consider modifying/expanding your data to allow your use-case. For example: you could stuff genre+lead into a single property that you just use for this filter.
"movie1": {
"genre": "comedy",
"name": "As good as it gets",
"lead": "Jack Nicholson",
"genre_lead": "comedy_Jack Nicholson"
}, //...
You're essentially building your own multi-column index that way and can query it with:
ref
.orderBy('genre_lead')
.equalTo('comedy_Jack Nicholson')
.on('child_added', function(snapshot) {
var movie = snapshot.val();
console.log(movie);
});
David East has written a library called QueryBase that helps with generating such properties.
You could even do relative/range queries, let's say that you want to allow querying movies by category and year. You'd use this data structure:
"movie1": {
"genre": "comedy",
"name": "As good as it gets",
"lead": "Jack Nicholson",
"genre_year": "comedy_1997"
}, //...
And then query for comedies of the 90s with:
ref
.orderBy('genre_year')
.startAt('comedy_1990')
.endAt('comedy_2000')
.on('child_added', function(snapshot) {
var movie = snapshot.val();
console.log(movie);
});
If you need to filter on more than just the year, make sure to add the other date parts in descending order, e.g. "comedy_1997-12-25". This way the lexicographical ordering that Firebase does on string values will be the same as the chronological ordering.
This combining of values in a property can work with more than two values, but you can only do a range filter on the last value in the composite property.
A very special variant of this is implemented by the GeoFire library for Firebase. This library combines the latitude and longitude of a location into a so-called Geohash, which can then be used to do realtime range queries on Firebase.
3. create a custom index programmatically
Yet another alternative is to do what we've all done before this new Query API was added: create an index in a different node:
"movies"
// the same structure you have today
"by_genre"
"comedy"
"by_lead"
"Jack Nicholson"
"movie1"
"Jim Carrey"
"movie3"
"Horror"
"by_lead"
"Jack Nicholson"
"movie2"
There are probably more approaches. For example, this answer highlights an alternative tree-shaped custom index: https://stackoverflow.com/a/34105063
If none of these options work for you, but you still want to store your data in Firebase, you can also consider using its Cloud Firestore database.
Cloud Firestore can handle multiple equality filters in a single query, but only one range filter. Under the hood it essentially uses the same query model, but it's like it auto-generates the composite properties for you. See Firestore's documentation on compound queries.
I've written a personal library that allows you to order by multiple values, with all the ordering done on the server.
Meet Querybase!
Querybase takes in a Firebase Database Reference and an array of fields you wish to index on. When you create new records it will automatically handle the generation of keys that allow for multiple querying. The caveat is that it only supports straight equivalence (no less than or greater than).
const databaseRef = firebase.database().ref().child('people');
const querybaseRef = querybase.ref(databaseRef, ['name', 'age', 'location']);
// Automatically handles composite keys
querybaseRef.push({
name: 'David',
age: 27,
location: 'SF'
});
// Find records by multiple fields
// returns a Firebase Database ref
const queriedDbRef = querybaseRef
.where({
name: 'David',
age: 27
});
// Listen for realtime updates
queriedDbRef.on('value', snap => console.log(snap));
var ref = new Firebase('https://your.firebaseio.com/');
Query query = ref.orderByChild('genre').equalTo('comedy');
query.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot movieSnapshot : dataSnapshot.getChildren()) {
Movie movie = dataSnapshot.getValue(Movie.class);
if (movie.getLead().equals('Jack Nicholson')) {
console.log(movieSnapshot.getKey());
}
}
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
Frank's answer is good but Firestore introduced array-contains recently that makes it easier to do AND queries.
You can create a filters field to add you filters. You can add as many values as you need. For example to filter by comedy and Jack Nicholson you can add the value comedy_Jack Nicholson but if you also you want to by comedy and 2014 you can add the value comedy_2014 without creating more fields.
{
"movies": {
"movie1": {
"genre": "comedy",
"name": "As good as it gets",
"lead": "Jack Nicholson",
"year": 2014,
"filters": [
"comedy_Jack Nicholson",
"comedy_2014"
]
}
}
}
For Cloud Firestore
https://firebase.google.com/docs/firestore/query-data/queries#compound_queries
Compound queries
You can chain multiple equality operators (== or array-contains) methods to create more specific queries (logical AND). However, you must create a composite index to combine equality operators with the inequality operators, <, <=, >, and !=.
citiesRef.where('state', '==', 'CO').where('name', '==', 'Denver');
citiesRef.where('state', '==', 'CA').where('population', '<', 1000000);
You can perform range (<, <=, >, >=) or not equals (!=) comparisons only on a single field, and you can include at most one array-contains or array-contains-any clause in a compound query:
Firebase doesn't allow querying with multiple conditions.
However, I did find a way around for this:
We need to download the initial filtered data from the database and store it in an array list.
Query query = databaseReference.orderByChild("genre").equalTo("comedy");
databaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
ArrayList<Movie> movies = new ArrayList<>();
for (DataSnapshot dataSnapshot1 : dataSnapshot.getChildren()) {
String lead = dataSnapshot1.child("lead").getValue(String.class);
String genre = dataSnapshot1.child("genre").getValue(String.class);
movie = new Movie(lead, genre);
movies.add(movie);
}
filterResults(movies, "Jack Nicholson");
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
Once we obtain the initial filtered data from the database, we need to do further filter in our backend.
public void filterResults(final List<Movie> list, final String genre) {
List<Movie> movies = new ArrayList<>();
movies = list.stream().filter(o -> o.getLead().equals(genre)).collect(Collectors.toList());
System.out.println(movies);
employees.forEach(movie -> System.out.println(movie.getFirstName()));
}
The data from firebase realtime database is as _InternalLinkedHashMap<dynamic, dynamic>.
You can also just convert this it to your map and query very easily.
For example, I have a chat app and I use realtime database to store the uid of the user and the bool value whether the user is online or not. As the picture below.
Now, I have a class RealtimeDatabase and a static method getAllUsersOnineStatus().
static getOnilineUsersUID() {
var dbRef = FirebaseDatabase.instance;
DatabaseReference reference = dbRef.reference().child("Online");
reference.once().then((value) {
Map<String, bool> map = Map<String, bool>.from(value.value);
List users = [];
map.forEach((key, value) {
if (value) {
users.add(key);
}
});
print(users);
});
}
It will print [NOraDTGaQSZbIEszidCujw1AEym2]
I am new to flutter If you know more please update the answer.
ref.orderByChild("lead").startAt("Jack Nicholson").endAt("Jack Nicholson").listner....
This will work.
I'm trying to get certain data from a json link:
bittrex.com/api/v1.1/public/getticker?market=BTC-DRS
in my node IRC bot using:
https://www.npmjs.org/package/node.bittrex.api
Part of the code:
var url = ('https://bittrex.com/api/v1.1/public/getticker?market=BTC-DRS');
bittrex.options({
'apikey' : settings.ticker.apikey,
'apisecret' : settings.ticker.secretkey,
'stream' : false,
'verbose' : false,
'cleartext' : true,
});
case 'ticker':
var user = from.toLowerCase();
bittrex.sendCustomRequest(url, function(ticker, err) {
if(err) {
winston.error('Error in !ticker command.', err);
client.say(channel, settings.messages.error.expand({name: from}));
return;
}
winston.info('Fetched Price From BitTrex', ticker);
client.say(channel, settings.messages.ticker.expand({name: user, price: ticker}));
});
break;
It works but outputs in IRC
[1:21am] <nrpatten> !ticker
[1:21am] <DRSTipbot> nrpatten The current DRS price at BitTrex {"success":true,"message":"","result":{"Bid":0.00000155,"Ask":0.00000164,"Last":0.00000155}}
I have used a couple of things to get it to show only "Last" from the reply but i keep getting errors.
Or get certain data from https://bittrex.com/api/v1.1/public/getmarketsummaries
Like any info i want from:
{"MarketName":"BTC-DRS","High":0.00000161,"Low":0.00000063,"Volume":280917.11022708,"Last":0.00000155,"BaseVolume":0.33696054,"TimeStamp":"2014-10-04T15:14:19.66","Bid":0.00000155,"Ask":0.00000164,"OpenBuyOrders":33,"OpenSellOrders":138,"PrevDay":0.00000090,"Created":"2014-06-18T04:35:38.437"}
Thanks for any help
Assuming you've parsed the JSON (e.g. via JSON.parse(str);), you just use whatever property name you want to get at. For example:
var info = JSON.parse('{"MarketName":"BTC-DRS","High":0.00000161,"Low":0.00000063,"Volume":280917.11022708,"Last":0.00000155,"BaseVolume":0.33696054,"TimeStamp":"2014-10-04T15:14:19.66","Bid":0.00000155,"Ask":0.00000164,"OpenBuyOrders":33,"OpenSellOrders":138,"PrevDay":0.00000090,"Created":"2014-06-18T04:35:38.437"}');
console.log(info.Bid);
Also, on an unrelated matter, typically callback parameters follow the error-first format (e.g. (err, result) instead of (result, err)) in order to be consistent with node core and most other modules on npm.
I'm using the last fm api to get data from a user but when using the User.getTopTracks method with the time period 1month, it returns nothing:
{
toptracks: {
#text: " ",
user: "RJ",
type: "1month",
page: "",
perPage: "",
totalPages: "",
total: "0"
}
}
This error does not occur when using similar methods (e.g. User.getTopAlbums)
This is a bug in the Last.fm API.
The issue used to exist for getTopArtists as well. It has been fixed for the artists call, but not for getTopTracks.
My solution is to use 3month when the user selects 1month. If I have time I will update the UI to work around this issue.
This question already has answers here:
Count of records by Date MongoDB
(5 answers)
Closed 2 years ago.
How do i convert the below mysql query to mongodb query: SELECT count(*) as count , DATE_FORMAT( timestamp, '%d-%c-%Y' ) as day, timestamp as tm FROM visits WHERE 1 GROUP BY day ORDER BY tm. I want to use this on a nodejs so i am using native mongodb.
Get the number of pageviews for each day in mongodb where each pageview is stored along with the timestamp.
Your question lacks any effort on your part and we rarely just "give" people the answer like this, however, this one time:
NB: you cannot yet manipulate dates to cast them to different formats without some manual work yourself of picking the parts out and rejoining them. Because of this I have left out the date formatting you did and just used it as an object.
db.visits.aggregate([
{
$project: {
date: {day: {$dayOfMonth: '$timestamp'}, month: {$month: '$timestamp'}, year: {$year: '$timestamp'}},
//day: {concat: [date.day,date.mont,date.year]}
}
},
{$group: {_id: '$date', tm: '$timestamp', count: {$sum:1}}}
])
I found a working mongodb query using mapreduce which gives me the output time as unix time rather than the format I had mentioned in the question. But this was the query that was sorting the time properly. I had tried mongo group query but it did not sort according to time. The working mongo query is :
db.visits.mapReduce(
function(){
day = Date.UTC(this.timestamp.getFullYear(), this.timestamp.getMonth(), this.timestamp.getDate());
emit({day: day}, {count: 1});
},
function(key, values) {
var count = 0;
values.forEach(function(v) {
count += v['count'];
});
return {count: count};
},
{
out : {inline:1},
sort:{_id:1}
}
);
I''m using EF to query the database using anonymous type.
here the code I use for EF
public JsonResult OverdueEventsCustom()
{
var eventCustomOverdue = _eventCustomRepository.FindOverdueEventsCustom();
return Json(eventCustomOverdue, JsonRequestBehavior.AllowGet);
}
public IQueryable<dynamic> FindOverdueEventsCustom()
{
DateTime dateTimeNow = DateTime.UtcNow;
DateTime dateTomorrow = dateTimeNow.Date.AddDays(1);
return db.EventCustoms.Where(x => x.DateTimeStart < dateTomorrow)
.Select(y => new { y.EventId, y.EventTitle, y.DateTimeStart});
}
Inspecting using the debugger I see the properties is in this format
Date = {16/08/2012 00:00:00}
The resultfor the JSON is
[{
"EventId": 1,
"EventTitle": "Homework Math",
"DateTimeStart": "\/Date(1345108269310)\/"
}, {
"EventId": 4,
"EventTitle": "Homework help with Annie",
"DateTimeStart": "\/Date(1345108269310)\/"
}, {
"EventId": 6,
"EventTitle": "Physic laboratory",
"DateTimeStart": "\/Date(1345108269310)\/"
}]
I need the the json in this format
"DateTimeStart": "(16/08/2012)"
Any idea what i'm doing wrong here? thanks for your help
Related articles
http://www.hanselman.com/blog/OnTheNightmareThatIsJSONDatesPlusJSONNETAndASPNETWebAPI.aspx
How do I format a Microsoft JSON date?
"\/Date(1345108269310)\/" is the correct way to pass a Date to javascript. The way I see it, you have two options here:
If you do not explicitly need the value as a date, you could just pass a string to the JSON variable, containing the pretty-printed date.
Something along the lines of:
DateTimeStart: String.Format("{0: dd-MM-yyyy}", myDate)
If you will still need to use the variable a a date in javascript (for calculations for example), the most consice and readably way would be to create a javascript function that converts said date into the pretty-printed string you want (I don't know if such a function already exists. It isn't too hard to create though:
function prettyDate(date) {
return date.getDate() + "-" + date.getMonth() + "-" + date.getFullYear();
}
I would suggest passing it along as a string from you code behind, as it is more readable. But that only works if you do not need to use the date except for displaying.