Repository navigation
Replies: 14 comments 16 replies
|
Here's what the binary pixies have to say about this, with a particular focus on starting with cli and commandenv, per Oliver's comment in #126 Proposal: retire
|
|
Lookup Matching, primarily tradedb.lookupPlace().
IMO best place to start untangling spaghetti and reward for doing so is here. Enabling all commands/scripts/tools/plugins to do lookups without the overhead of a TradeDB construction, at raw db speeds. From there we'd be positioned to review plugins, scripts, and commands for now more visibly redundant uses of TradeDB/wantsTradeDB=True. The current TradeORM lookups definitely aren't perfect: "3583/Antonio" tells me 3583 matches more than one system. I reached an impasse in my knowledge/determination to launch TradeORM where I realized that doing a naive db-crawl of names would suck. I know zippity-flip-squat about text indexing/searching, tbh. Writing lookupPlace left a lasting sense of dread :) Naive, ai/google-lookup free notion: every table gets a "search_name" column which is the fully-formed name in all caps separated by a character that'll have to be other-than-slash since many station names include a slash. perhaps: f"{grouping}:/:{name}" even where there's no grouping (enabling reuse of a single search algo). or f'"{quote_escaped_grouping}","{quote_escaped_name}"'. slightly more expensive to build, but nobody should be doing repeated calls to this in any kind of tight loop. who cares if it takes 1250ms to match a pathologically bad input one time before telling you "'a'? yeah, that matches like a billion station names" ^^^--- I'm not proposing this ---^^^ this is my pixel fairy "this is probably the threshold of stupid". It's just a base to touch against when we respectively go away and get claude / perplexity / gipeetee's advice on how to do text indexing in an sql database that'll work across backends :) |
|
"and likely a “return IDs not ORM instances” option for cheap downstream use." For reasons, I suspect that's premature optimization. Why do you want, for example, a category id or a station id? There's nothing you can innately do with that. You can't filter against it. If you can, if you already retrieved a collection of station_ids, then now you're doing a "user space join" which means you left Python into C one time, got data, brought it into Python, manipulated it, took more data out into C and brought data back, and then compared them, when you probably wanted to do that all in one go. TradeDB's sin was trying to avoid that by just bringing all the data out one time up front :) SqlAlchemy/TradeORM is the realization you don't need to, you can talk to the data hyper-efficiently without pre-loading all of it. TradeDB just never got the smarts to realize that part of itself, and we shouldn't invest the time making it have them. This is something that's been buzzing at the back of my head and it's coming together now. What we actually want is to help surface more SQL richness. Think of the "avoids" list we build - we don't actually want or need that data for anything beyond passing it to the database, and we get it from the database. It's in python because the original author is a programmer who thought like a programmer. TradeORM should encourage thinking about data. You want to make a list of things to avoid? Add a CTE or a temp table. ... don't even need to complicate that last part by making it conditional. the query planner will deal with that if the table is empty. boosh. work done for us, and - back in python - we don't have to pass some table of indexes to be avoided around. Infact, we can actually use SQLite's aliasing to create pseudo tables/views that we pass around instead: did you see me mention avoids? no, because I either pass SA_Item or I pass an alias/cte that's SA_Item filtered by an Avoiding table I created... Stopping because much typed sanity check required :) |
|
^- we could even in the schema define one or more common temporary tables that can be populated and stuff will just automatically use them if they're populated. |
|
Todo: Persist the spatial grid, when I first created it, it was changing all the time. |
|
Again consulting the pixies. Btw... you may be a programmer thinking like a programmer, but that may be more useful me as a sysadmin thinking like a sysadmin. On the other hand, me wanting a robust DB solution was what triggered my DB work, so I guess it's all good. Subject of which, I totally agree we should be using the database as far as possible, to deal with data. Anyway, the pixies are also in basic agreement: 1) “Lookup matching is the best place to start”Agree. Also: we currently already have a “fast validator” hook ( 2) TradeORM lookups are basic / “3583/Antonio” problemAlso agree. Right now TradeORM is doing “simple query then ambiguity”, but 3) “Naive DB crawl of names would suck”Yes, but we don’t need a “crawl everything into Python” to avoid that. The pragmatic DB-side approach is:
That keeps the resolver logic consistent across backends, and avoids “pull everything, then normalise” as an algorithm. I’d probably avoid a single composite
Instead: keep separate columns, e.g.:
(If we want to go further later: SQLite FTS5 / MariaDB FULLTEXT can be optional accelerators, but I wouldn’t make that a blocker for parity.) 4) IDs vs ORM instancesI think you’re right to push back on “IDs everywhere” if the caller then does user-space joins. The actual goal should be: compose queries, don’t shuttle half-results through Python. However, there are two distinct contexts here:
So rather than “return IDs not ORM instances”, the design target might be:
That achieves your “SQL richness” point without prematurely micro-optimising. 5) Temp tables / CTEs for avoids etcConceptually: yes. “Avoid lists live in Python because programmer brain” is basically the right diagnosis. Practically: temp tables are connection-scoped, and SQLAlchemy sessions can hop connections unless we’re careful, so we need to be disciplined about lifecycle (single session/transaction/connection for the query pipeline). A lower-risk stepping stone is:
Proposed next concrete steps
The pixies want to go in clear steps and have a morbid fear of doing too much at once. Starting with 2 & 3 above for place lookups gives us an immediate win. |
|
Here's some specific actionable goals to get us going: PR 1: Make “fast validation” actually fast
This is the “startup speed win when you type daftness” you were pointing at, and it’s mostly wiring/control-flow, not “rewrite the world”. PR 2: Define resolver semantics (contract) + implement parity
Without that, every migration step becomes a regression lottery. PR 3: Move
|
|
no tdgui isn't gui-like
it's a gui, using tkinter
…On Sun, Dec 14, 2025 at 4:25 PM Oliver Smith ***@***.***> wrote:
tdgui: the intent of the separation between run and render was supposed to
help there; I think I had half a mind there'd be "run_cmd.py" and
"run_gui.py" which inherited it for the run method and then overwrote the
render() method. I don't think anything like rich existed back then, but
an easier path to the migration might be to look at something like textual
first (https://textual.textualize.io/, same people as rich) to build a
long-running cli command with a gui-like interface.
—
Reply to this email directly, view it on GitHub
<#245 (reply in thread)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AANHHYEW423JRJ5KJSKORKD4BXWV5AVCNFSM6AAAAACO6S7Z72VHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTKMRVGQZTENY>
.
You are receiving this because you commented.Message ID:
***@***.***
com>
--
mu'o mi'e .aionys.
.i.e'ucai ko cmima lo pilno be denpa bu .i doi.luk. mi patfu do zo'o
(Come to the Dot Side! Luke, I am your father. :D )
|
|
For both the feat/trade-game and dev/local-cmd-orm-refactor I've been trying to bring the TradeORM lookup features up to scratch, but was unprepared for how complex lookupPlace is. I explored just preloading the relevant tables so it could do the search on those, but it makes the TradeORM entry slow enough that it doesn't feel like the migration is getting you anything. Wanted to check what @eyeonus and @Tromador want to bring forward from the current name lookup stuff. -- brain dump/deep dive, ignore if I'm on the wrong track. The fuzzy match is always going to be expensive one way or another - you either have to table scan or build some kind of index or install some kind of db plugin; since this is cli-facing, that can potentially afford to be sqlite specific? If you're using Maria/Postgres are you needing to be able to find sol/abraham lincoln by typing just I'm not convinced that the current approach, to try hard to find an ambiguity for you, is helpful. We provide disambiguation constructs. We have some existing syntax for disambiguation (@, /), is it worth making the default behavior be simple precise lookups with an operator that denotes "like this"? That is: I think trying more aggressively to find a unique match and stop is going to be more user-friendly as the player-input names become more prevalent. Some terms: unit :- the model you're trying to lookup (Station, Item, or System) depending on the type of lookup System Lookup: unit = System, group = None, fold = System The fold matters for '@' and '/' prefixes; lookup_station("@sol/linc"), lookup_item("@slave/slave") -> "@sol" and "@slave" are group terms, possible syntaxes: "@soansdo" -> fold exact "soandso" or prefix (like "soandso%") or anchored-fuzzy (must start with 's') ^- soandso may include @n syntax for disambiguation "/soandso" -> unit exact "soandso", unit prefix, unit anchored-fuzzy, unit open fuzzy Whichever way you shape it you seem to end up with some anomalies, especially now that stations can (and do) include '/' in the names: |
|
So I mean... ok. When we say fuzzy, let's be honest, we're not dealing with some well known Levenshtein algorithm. We're dealing with a mini-language parser + ranking engine + ambiguity explainer. To say it's "complex" is not exaggeration.
So if we want behavioural parity, we’re not porting “a lookup”, we’re porting a We can't just throw a couple SQLA filters at this and cheer a job well done. Somewhere somehow we have to pay a price, the question is where. A real statement of problem is probably something like So we need a name-resolution-strategy that's DB-friendly. I suspect the pin we're dancing on is we can have fast or we can have global substring magic (e.g. hamlinc), unless we build extra indexing structures.
The decision is where we want to land on that spectrum. Which I shall address later. I've got a pile of notes to condense down, but my cat is going insane with "it's bedtime!!!" noises, which translated mean "I want my bedtime treat NOW!". And as it's 4am, she's not wrong. So expect a lot more on the subject, after sleep! |
|
Perhaps we should add "additional indexes" to the todo, for which we might want to do explains on the queries and figure out what is worth the index-build cost. I have a hunch we'll need to guide import plugins towards change-avoidance to reduce index churn, but if we structure them well it shouldn't be too awful. Things I suspect we might want are surfacing units in supply, definitely think there are huge gains to be had from splitting StationItem into separate supply/demand, etc, etc. |
|
This is what I'm thinking 1) Define the lookup contract we want (not “whatever lookupPlace happened to do”)How about we break it down into an explicit ladder, fast stuff then slow stuff.
This clears up the fuzziness of our fuzziness requirement. 2) Narrow scope before doing anything expensiveThis gives us a practical DB advantage:
3) Stop bikeshedding string operators; make “slow mode” a flag if neededGiven / appears in names, don’t add clever syntactic meanings that will bite us later. 4) Commit to measuring with EXPLAIN and a tiny benchmark listKFSOne suggested “add indexes, do explains”. Let's turn that into a concrete task:
5) Identify which legacy behaviours we must carryWe have a key question to force alignment:
For me, I think making it opt-in is a really cool idea. Or opt-out if you want to keep current default behaviour. Either way, let's take the expensive thing and make it an option. |
|
There's probably a spin-off discussion to be had about establishing some sane default values to improve performance of things like "run"; for instance, having a sane "minimum profit" (gain) value. I mean, I've been thinking: an item with a max-demand-price of 400cr can never meet a gain of 401cr. factor in the minimum supply-price and... (if we add 'units' to the supply index) Does the few ms to query that give you a leg-up in reducing the search space for worthwhile trades? (*** this is not a well thought out actionable just spitballing) How is this relevant to TradeORM? Loosely, a general view towards "start from a higher bar" with TradeORM to emphasize performance for common use and give edge-cases a price rather than making all queries pay them? If you wanna know how to get from 100 credits to 1000, don't be searching the entire galaxy. |
|
In my fork I now have all basic shapes in the new planner code running through tradeorm.py 1: Dedicated rocket speed single hop planner for both ends, one end or neither end anchored. In all cases, it's faster (often a LOT) faster. Queries all complete (reasonable ones, I've not tried 20 hops!) in places where current TD doesn't, or at least not in time I have patience for. Profit is comparable. Sometimes a bit less, often more. As this covers all possible shapes and the rest is adding remaining filters, I think this discussion is now moot and so I'm closing it. |
Uh oh!
There was an error while loading. Please reload this page.
Background:
The original concept of TradeDB() was to load the data and use this object to answer multiple questions.
create DB
answer lots of questions
maybe never exit
I think we're all in agreement that TradeDB() is a big old monolithic device that never really achieved its aim, despite best of intentions and now, some years later with the exponential explosion of data it's using a lot of memory, pute, time to load up to answer just one question at a time.
With the SQLA refactor done, we can now use ORM to abstract the data source away and use that instead to peel away the Legacy TradeDB().
Brief:
Discuss how we move away from TradeDB() in an orderly fashion, which doesn't (we hope) break many (or any) things, keeping (as far as we can) backwards compatibility and improve Trade: Dangerous by doing so.
All reactions