Collection find gives undefined. Unable to access fields from a collections


#1

I’m trying to get a field value from a single row of data returned from a mongo collection.

var json= Locs.find({latest: "1"} , {fields: {lat:1}}).fetch();

But when I try to actually access the field : ‘lat’ , I get

TypeError: Cannot read property 'lat' of undefined

I’ve tried JSON.parse(json) but the console tells me :

SyntaxError: Unexpected end of JSON input

If I just return the variable json then I get :

[{"lat":"051.159294","_id":{"_str":"5aa00a870adcf3bb649a6251"}}]

So that at least works. But what is wrong (with me probably).

I’ve been googling for 4 hours now and am tearing out what’s left of my hair.


#2

Hmm, how’re you trying to access the variable? you’re getting array when using find/fetch, so you need to iterate over it or use an index.


#3

Well, I’ve tried practically every combination of find/findOne along with
return json; (which returns the json object - so kinda works)
or
return json[0]; (this works too)
or
return json[0].lat; (which gives undefined error)

fetch() doesn’t work with findOne either for some reason.

Every single thing that I’ve read off the internet/google does not work. All I want is to get the numeric value of the variable ‘lat’.


#4

Are you trying to access this data from the client or the server? This is going to make a big difference because on the client the data may not be available because it hasn’t been sent by a publication, or you could just be running into a race condition where you are trying to display but not waiting for the data to be available first and sometimes it displays because it arrived fast enough, and other times it doesn’t because the data arrived to the client yet.


#5

Hi & thanks , copleyja. It’s running on the client side in a helper function.

            liveLat(){
                var json= Locs.findOne({latest: "1"} , {fields: {lat:1}});
                var str=JSON.stringify(json);
                //var ejson = EJSON.parse(str);
                //var obj = JSON.parse(str);
                console.log(str);
                console.log(json);

The annoying thing is that the console will show both the string AND the json object. But there just doesn’t seem to be any way whatsoever to access the field named ‘lat’ by any conventional means. Both the JSON and EJSON parsers fail with “unexpected data”.
I take you point about race condition but feel that if the console log shows the data I expect then why can I not access it using one of the [many] methods such asx=json.lat or json.lat.value or json["lat"] or json["lat"].value … or whatever. It’s driving me 7 hours of nuts now!!
Accept that I am fairly new to meteor (usually java/c++/android,me) but either I have so utterly missed something or meteor is just the most counter intuitive thing I’ve ever encountered. I do obviously realise it must be me though.


#6

Can you show the result of this?


#7

Sorry for elapsed time (needed sleep then wake early again for Chinese GP).

{lat: "052.159294", _id: M…D.ObjectID}
lat
:
"052.159294"
_id
:
MongoID.ObjectID
_str
:
"5aa00a870adcf3bb649a6251"
__proto__
:
clone
:
ƒ ()
equals
:
ƒ (other)
getTimestamp
:
ƒ ()
toHexString
:
ƒ ()
toJSONValue
:
ƒ ()
toString
:
ƒ ()
typeName
:
ƒ ()
valueOf
:
ƒ ()
constructor
:
ƒ (hexString)
__proto__
:
Object
__proto__
:
Object

stringify = {"lat":"052.159294","_id":{"_str":"5aa00a870adcf3bb649a6251"}}


#8

json.lat must work. And json[0] should give undefined.


#9

Hi bordalix.

I’m afraid it doesn’t (and I really have tried everything!). I’m taking the data out in the html at the moment by doing this
{{$ctrl.liveLat.lat}}
which is the only way I can get it working. But trouble is I need to do a serious amount of computation in the js on the latitude and longitude fields (convert to UK osgrid refs) and so I need to access them there.
It’s something I’m doing wrong but cannot see due to my lack of Meteor experience but as purely a sidenote here I solemnly promise I spent 8 hours trying to research just this alone and got nowhere : answers like yours came around and I tried them only to get ‘undefined’.
Research an issue like this for c++, java, unix, android and you’ll get something between 1-15 minutes… but this!?!?!
Meteor/angular/mongo/node.js/javascript/json/ejson seem incredibly difficult to gain decent/incisive documentation or online help for [presumably because there’s so many different things in the ‘field of play’ and with so many different versions of them all too] without resorting to a direct question on a forum like this. Soorry for that. :blush:


#10

As far as I can see this should work… do you have a public repo that I could look at to try to diagnose the issue?


#11

@babysparrow maybe I get it wrong. You’re getting errors on the parsers, right? If so, why are you using parsers? You already have an object ‘json’, where you can access ‘lat’ and ‘lon’ directly:

 var json = Locs.findOne({latest: "1"} , {fields: {lat:1, lon:1}});
 var lat = json.lat;
 var lon = json.lon;

Does it help?


#12

@bordalix my bad on the confusion there. I was merely attempting to stringify then reparse (json) the data just to see what happened. The stringify worked, but the JSON.parse always gives an error. I felt for a couple of hours that this was my biggest clue … it probably still is. [sidenote: I don’t want to end up with loads of logic in the html if can be avoided … not exactly MVC, IMHO , AFAIK ; starting to remind me of the early bad old days of html/java/jsp … yes I’m old… started with COBOL, me].

This code :

            liveLoc(){
                    
                jsonLoc = Locs.findOne({latest: "1", target:this.Gtarget} , {fields: {lat:1,lon:1,date:1}});

                console.log(this.Gmode);
                console.log(this.Gtarget);
                console.log(jsonLoc);
                var testlat=jsonLoc.lat;    This is line 64 in tracker.js**
                console.log(testlat);
                return jsonLoc;
            }

gives this error :

modules.js?hash=1ab1f541404583097e9129ea3e137c42a9e333d8:15833 TypeError: Cannot read property 'lat' of undefined
    at trackerCtrl.liveLoc (tracker.js:64)

Yes it’s observed (it has to be)… does that make a difference?

Reply to copleykj to follow… & thx again both.


#13

Second point. I’m aware that depending on what did the insert to the mongo collection then _id may be a string or it may be an object and wonder if this is causing the json parser to fail. My id’s are all objects rather than strings. I cannot exclude the _id because the observer gives an error.


#14

It looks like findOne() didn’t return anything.
Is there any document with that conditions?
Did you wait for the end of the subscription?


#15

Yeah, it does :

{date: Sun Apr 15 2018 16:26:32 GMT+0100 (GMT Daylight Time), lat: "052.149232", lon: "-001.892217", _id: M…D.ObjectID}

And I can get to lat in the html with :

{{$ctrl.liveLoc.lat}}

This is the frustrating thing about it all !! :persevere:


#16

Additionally. Am not using publish/subscribe. Am still on autopublish (or whatever that is called) and this app will probably stay that way. A few months ago I did the meteor/blaze todo app which takes autopublish out etc, etc. Then I did the same again with angular. But I don’t need to do that so far for this app - probably never will. If that makes a difference (and I don’t see why it should) then feel free to dress me down.


#17

I have a feeling that this is running before the published data is ready. Thanks to the reactivity, it will re-run correctly when the data is available.
All you should need to do is check if the document is undefined by nesting it inside an #if:

liveLoc() {
    return Locs.findOne({latest: "1", target:this.Gtarget} , {fields: {lat:1,lon:1,date:1}});
}
// Some html
<div class="loc">
  {{#if liveLoc }}{{ liveLoc.lat }}, {{ liveLoc.lon }}{{/if}}
</div>

#18

I thought I should join in this conversation. It may be helpful to recap a few things first:

  1. collection.find() returns a cursor. You can’t access documents or document properties directly from a cursor. You need to use an appropriate cursor method (e.g. .fetch(), .map() or forEach()).
  2. From (1), find().fetch() returns an array of documents, as others have already pointed out.
  3. collection.findOne() is exactly equivalent to collection.find().fetch()[0]. It returns the first matching document from the cursor identified by collection.find().
  4. Returned documents are not JSON-encoded - they are JavaScript objects. In other words, JSON.parse will not work.
  5. There is little point using the fields modifier if you are using autopublish, since that publishes all fields by default and it’s only really useful in a publication to minimise network use.
  6. If the document is available on the client (and it may not be), then I would expect a query of the form const lat = Locs.findOne(selector).lat; to work as expected. Equally, const lat = Locs.find(selector).fetch()[0].lat; should work for reason (2).

If the document is not available on the client when you expect it, then @coagmano’s answer above will ensure that it reactively updates for presentation when it does arrive. There are other ways of achieving that, but that’s a really easy thing to test.

Failing that, I’d be interested in double-checking what’s actually in the database. To do that, start your application and in another terminal in the same application root, do meteor mongo. You will then have a MongoDB command window. In that, do db.locs.findOne({latest: "1"}) and paste the result here. (I’ve assumed locs is the name of your collection - if not, use the correct name).


#19

Thx @robfallows, my current code is this:

            liveLoc(){
                    
                jsonLoc = Locs.findOne({latest: "1", target:this.Gtarget} , {fields: {lat:1,lon:1,date:1}});

                console.log(this.Gmode);
                console.log(this.Gtarget);
                console.log(jsonLoc);
                //var testlat=jsonLoc.lat;
                //console.log(testlat);
                return jsonLoc;

And I access lat and lon in the html (templated) using this :
{{$ctrl.liveLoc.lat}}

There’s never been an issue with that. It works.
So to draw a line under some of the other/older posts … that’s the code and it works (I’m also using findOne and I’m aware of cursor with find() without .fetch() .

Yes, the collection is called locs and I’m currently using meteor/mongo on :3001 and here is the row/document which it does retrieve :

{
    "_id" : ObjectId("5ad36f283e778317b8c04014"),
    "target" : "Sparrow",
    "latest" : "1",
    "lat" : "053.179232",
    "lon" : "-001.296217",
    "date" : ISODate("2018-04-15T15:26:32.241Z")
}

So far … all good.
It’s just that I cannot seem to access the fields lat or lon within the helper function. Maybe I shouldn’t be trying to in the first place? I was just wandering along writing code (in not my most familiar language) when I tried to do this : return jsonLoc.lat … and 2 days later I’m feeling really guilty that I’m wasting peoples time on something that is certainly my own fault. But I just don’t understand why.


#20

Cool. I was going by this, which suggested to me that you didn’t understand the connection:

Are you saying that this works in your helper: console.log(jsonLoc);, but this will not: console.log(jsonLoc.lat);?